import { useCopilotStore } from "@/stores/copilot.store";
import type { ToolCall } from "@/stores/copilot.store";
import { Call } from "@wailsio/runtime";

/**
 * Fully qualified Go method name prefix for AIService bindings.
 * Wails v3 ByName format: 'package.struct.method'
 */
const AI = "github.com/vstockwell/wraith/internal/ai.AIService";

/** Call a bound Go method on AIService by name. */
async function callAI<T = unknown>(method: string, ...args: unknown[]): Promise<T> {
  return Call.ByName(`${AI}.${method}`, ...args) as Promise<T>;
}

/** Shape returned by Go AIService.SendMessage. */
interface ChatResponse {
  text: string;
  toolCalls?: {
    name: string;
    input: unknown;
    result: unknown;
    error?: string;
  }[];
}

/**
 * Composable providing real Wails binding wrappers for the AI copilot.
 *
 * Calls the Go AIService via Wails v3 Call.ByName. SendMessage blocks
 * until the full response (including tool-use loops) is complete.
 */
export function useCopilot() {
  const store = useCopilotStore();

  /**
   * Process a user message by calling the real Go backend.
   * The backend blocks until the full response is ready (no streaming yet).
   */
  async function processMessage(text: string): Promise<void> {
    store.isStreaming = true;

    // Ensure we have a conversation
    if (!store.activeConversationId) {
      try {
        const convId = await callAI<string>("NewConversation");
        store.activeConversationId = convId;
      } catch (err) {
        store.messages.push({
          id: `msg-${Date.now()}`,
          role: "assistant",
          content: `Error creating conversation: ${err}`,
          timestamp: Date.now(),
        });
        store.isStreaming = false;
        return;
      }
    }

    try {
      const response = await callAI<ChatResponse>(
        "SendMessage",
        store.activeConversationId,
        text,
      );

      // Build the assistant message from the response
      const toolCalls: ToolCall[] | undefined = response.toolCalls?.map(
        (tc) => ({
          id: `tc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
          name: tc.name,
          input: (tc.input ?? {}) as Record<string, unknown>,
          result: tc.result,
          status: (tc.error ? "error" : "done") as "done" | "error",
        }),
      );

      store.messages.push({
        id: `msg-${Date.now()}`,
        role: "assistant",
        content: response.text || "",
        toolCalls: toolCalls,
        timestamp: Date.now(),
      });
    } catch (err) {
      store.messages.push({
        id: `msg-${Date.now()}`,
        role: "assistant",
        content: `Error: ${err}`,
        timestamp: Date.now(),
      });
    } finally {
      store.isStreaming = false;
    }
  }

  /** Begin the OAuth login flow (opens browser). */
  async function startLogin(): Promise<void> {
    try {
      await callAI("StartLogin");
    } catch (err) {
      console.error("StartLogin failed:", err);
    }
  }

  /** Check whether the user is authenticated. */
  async function checkAuth(): Promise<boolean> {
    try {
      const authed = await callAI<boolean>("IsAuthenticated");
      store.isAuthenticated = authed;
      return authed;
    } catch {
      return false;
    }
  }

  /** Log out and clear tokens. */
  async function logout(): Promise<void> {
    try {
      await callAI("Logout");
      store.isAuthenticated = false;
      store.clearHistory();
    } catch (err) {
      console.error("Logout failed:", err);
    }
  }

  /** Sync the model setting to the Go backend. */
  async function setModel(model: string): Promise<void> {
    try {
      await callAI("SetModel", model);
      store.model = model;
    } catch (err) {
      console.error("SetModel failed:", err);
    }
  }

  /** Load the current model from the Go backend. */
  async function getModel(): Promise<string> {
    try {
      const m = await callAI<string>("GetModel");
      store.model = m;
      return m;
    } catch {
      return store.model;
    }
  }

  return { processMessage, startLogin, checkAuth, logout, setModel, getModel };
}
