/**
 * @mizan/tauri-transport — routes Mizan calls through Tauri's IPC
 * instead of HTTP fetch. Paired with the `mizan-tauri` Rust plugin
 * (which exposes a single `mizan_invoke` command).
 *
 * Usage:
 *
 *   import { configure } from '@mizan/base'
 *   import { tauriTransport } from '@mizan/tauri-transport'
 *
 *   configure({ transport: tauriTransport() })
 *
 * The transport keeps the same protocol surface as the HTTP transport
 * (call/fetch envelopes, {result, invalidate, merge} response shape),
 * so the codegen output and React adapter are unchanged — only the
 * wire channel differs.
 */

import { invoke } from '@tauri-apps/api/core'
import { MizanError, type MizanCallResponse, type MizanTransport } from '@mizan/base'

/** Plugin-prefixed Tauri command — `init()` on the Rust side installs
 * the plugin under the name `mizan`, so the dispatch command is
 * reachable from JS as `plugin:mizan|mizan_invoke`. */
const COMMAND = 'plugin:mizan|mizan_invoke'

type TauriError = {
    code?: string
    message?: string
    details?: unknown
}

/**
 * Wrap a `mizan-tauri` error payload into a `MizanError` so consumers
 * see one error surface regardless of transport. Tauri's invoke()
 * rejects with the raw `Err` payload from the Rust command; we re-shape
 * it to the same `{error: {code, message, details}}` envelope the HTTP
 * transport surfaces.
 */
function wrapError(raw: unknown): MizanError {
    if (raw && typeof raw === 'object') {
        const err = raw as TauriError
        const body = JSON.stringify({ error: { code: err.code, message: err.message, details: err.details } })
        // 0 means "no HTTP status" — the IPC transport bypassed the
        // protocol's HTTP layer. The error envelope still carries
        // .code/.message/.details so consumers don't have to special-case.
        return new MizanError(0, body)
    }
    return new MizanError(0, JSON.stringify({ error: { message: String(raw) } }))
}

/**
 * Build a Mizan transport that routes through Tauri's IPC. Install via:
 *
 *   import { configure } from '@mizan/base'
 *   import { tauriTransport } from '@mizan/tauri-transport'
 *   configure({ transport: tauriTransport() })
 */
export function tauriTransport(): MizanTransport {
    return {
        async call(fnName, args): Promise<MizanCallResponse> {
            try {
                const data = await invoke<MizanCallResponse>(COMMAND, {
                    envelope: { op: 'call', fn: fnName, args },
                })
                return data
            } catch (e) {
                throw wrapError(e)
            }
        },
        async fetch(contextName, params) {
            try {
                return await invoke(COMMAND, {
                    envelope: { op: 'fetch', context: contextName, params: params ?? {} },
                })
            } catch (e) {
                throw wrapError(e)
            }
        },
    }
}
