import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { Call } from "@wailsio/runtime";
import { useConnectionStore } from "@/stores/connection.store";
import type { ThemeDefinition } from "@/components/common/ThemePicker.vue";

const APP = "github.com/vstockwell/wraith/internal/app.WraithApp";

export interface Session {
  id: string;
  connectionId: number;
  name: string;
  protocol: "ssh" | "rdp";
  active: boolean;
  username?: string;
}

export interface TerminalDimensions {
  cols: number;
  rows: number;
}

export const useSessionStore = defineStore("session", () => {
  const sessions = ref<Session[]>([]);
  const activeSessionId = ref<string | null>(null);
  const connecting = ref(false);
  const lastError = ref<string | null>(null);

  /** Active terminal theme — applied to all terminal instances. */
  const activeTheme = ref<ThemeDefinition | null>(null);

  /** Per-session terminal dimensions (cols x rows). */
  const terminalDimensions = ref<Record<string, TerminalDimensions>>({});

  const activeSession = computed(() =>
    sessions.value.find((s) => s.id === activeSessionId.value) ?? null,
  );

  const sessionCount = computed(() => sessions.value.length);

  function activateSession(id: string): void {
    activeSessionId.value = id;
  }

  async function closeSession(id: string): Promise<void> {
    const idx = sessions.value.findIndex((s) => s.id === id);
    if (idx === -1) return;

    const session = sessions.value[idx];

    // Disconnect the backend session
    try {
      await Call.ByName(`${APP}.DisconnectSession`, session.id);
    } catch (err) {
      console.error("Failed to disconnect session:", err);
    }

    sessions.value.splice(idx, 1);

    if (activeSessionId.value === id) {
      if (sessions.value.length === 0) {
        activeSessionId.value = null;
      } else {
        const nextIdx = Math.min(idx, sessions.value.length - 1);
        activeSessionId.value = sessions.value[nextIdx].id;
      }
    }
  }

  /** Count how many sessions already exist for this connection (for tab name disambiguation). */
  function sessionCountForConnection(connId: number): number {
    return sessions.value.filter((s) => s.connectionId === connId).length;
  }

  /** Generate a disambiguated tab name like "Asgard", "Asgard (2)", "Asgard (3)". */
  function disambiguatedName(baseName: string, connId: number): string {
    const count = sessionCountForConnection(connId);
    return count === 0 ? baseName : `${baseName} (${count + 1})`;
  }

  /**
   * Connect to a server by connection ID.
   * Multiple sessions to the same host are allowed (MobaXTerm-style).
   * Each gets its own tab with a disambiguated name like "Asgard (2)".
   */
  async function connect(connectionId: number): Promise<void> {
    const connectionStore = useConnectionStore();
    const conn = connectionStore.connections.find((c) => c.id === connectionId);
    if (!conn) return;

    connecting.value = true;
    try {
      if (conn.protocol === "ssh") {
        let sessionId: string;
        let resolvedUsername: string | undefined;

        try {
          // Try with stored credentials first
          sessionId = await Call.ByName(
            `${APP}.ConnectSSH`,
            connectionId,
            120, // cols (will be resized by xterm.js fit addon)
            40,  // rows
          ) as string;
        } catch (sshErr: any) {
          const errMsg = typeof sshErr === "string" ? sshErr : sshErr?.message ?? String(sshErr);

          // If no credentials, prompt for username/password
          if (errMsg.includes("NO_CREDENTIALS") || errMsg.includes("unable to authenticate")) {
            const username = prompt(`Username for ${conn.hostname}:`, "root");
            if (!username) throw new Error("Connection cancelled");
            const password = prompt(`Password for ${username}@${conn.hostname}:`);
            if (password === null) throw new Error("Connection cancelled");

            resolvedUsername = username;
            sessionId = await Call.ByName(
              `${APP}.ConnectSSHWithPassword`,
              connectionId,
              username,
              password,
              120,
              40,
            ) as string;
          } else {
            throw sshErr;
          }
        }

        // Try to get username from connection options if not already resolved
        if (!resolvedUsername && conn.options) {
          try {
            const opts = JSON.parse(conn.options);
            if (opts?.username) resolvedUsername = opts.username;
          } catch {
            // ignore malformed options
          }
        }

        sessions.value.push({
          id: sessionId,
          connectionId,
          name: disambiguatedName(conn.name, connectionId),
          protocol: "ssh",
          active: true,
          username: resolvedUsername,
        });
        activeSessionId.value = sessionId;
      } else if (conn.protocol === "rdp") {
        // Call Go backend — resolves credentials, builds RDPConfig, returns sessionID
        const sessionId = await Call.ByName(
          `${APP}.ConnectRDP`,
          connectionId,
          1920, // initial width — resized by the RDP view on mount
          1080, // initial height
        ) as string;

        sessions.value.push({
          id: sessionId,
          connectionId,
          name: disambiguatedName(conn.name, connectionId),
          protocol: "rdp",
          active: true,
        });
        activeSessionId.value = sessionId;
      }
    } catch (err: any) {
      const msg = typeof err === "string" ? err : err?.message ?? String(err);
      console.error("Connection failed:", msg);
      lastError.value = msg;
      // Show error as native alert so it's visible without DevTools
      alert(`Connection failed: ${msg}`);
    } finally {
      connecting.value = false;
    }
  }

  /** Apply a theme to all active terminal instances. */
  function setTheme(theme: ThemeDefinition): void {
    activeTheme.value = theme;
  }

  /** Update the recorded dimensions for a terminal session. */
  function setTerminalDimensions(sessionId: string, cols: number, rows: number): void {
    terminalDimensions.value[sessionId] = { cols, rows };
  }

  /** Get the dimensions for the active session, or null if not tracked yet. */
  const activeDimensions = computed<TerminalDimensions | null>(() => {
    if (!activeSessionId.value) return null;
    return terminalDimensions.value[activeSessionId.value] ?? null;
  });

  return {
    sessions,
    activeSessionId,
    activeSession,
    sessionCount,
    connecting,
    lastError,
    activeTheme,
    terminalDimensions,
    activeDimensions,
    activateSession,
    closeSession,
    connect,
    setTheme,
    setTerminalDimensions,
  };
});
