import { writable, derived } from "svelte/store";
import { listen } from "@tauri-apps/api/event";

export type LogLevel = "debug" | "info" | "warn" | "error";

export interface LogEntry {
  id: string;
  timestamp: Date;
  level: LogLevel;
  message: string;
  source: "frontend" | "backend";
}

interface DebugConsoleState {
  logs: LogEntry[];
  isOpen: boolean;
  maxLogs: number;
  filterLevel: LogLevel | "all";
  autoScroll: boolean;
}

const MAX_LOGS = 1000; // Circular buffer size

function createDebugConsoleStore() {
  const { subscribe, update } = writable<DebugConsoleState>({
    logs: [],
    isOpen: false,
    maxLogs: MAX_LOGS,
    filterLevel: "all",
    autoScroll: true,
  });

  let logCounter = 0;

  function addLog(level: LogLevel, message: string, source: "frontend" | "backend") {
    update((state) => {
      const newLog: LogEntry = {
        id: `log-${Date.now()}-${logCounter++}`,
        timestamp: new Date(),
        level,
        message,
        source,
      };

      const updatedLogs = [...state.logs, newLog];

      // Implement circular buffer - remove oldest if exceeding max
      if (updatedLogs.length > state.maxLogs) {
        updatedLogs.shift();
      }

      return { ...state, logs: updatedLogs };
    });
  }

  // Override console methods to capture frontend logs
  const originalConsole = {
    log: console.log,
    info: console.info,
    warn: console.warn,
    error: console.error,
    debug: console.debug,
  };

  function setupConsoleCapture() {
    console.log = (...args: unknown[]) => {
      originalConsole.log(...args);
      addLog("info", args.map((arg) => String(arg)).join(" "), "frontend");
    };

    console.info = (...args: unknown[]) => {
      originalConsole.info(...args);
      addLog("info", args.map((arg) => String(arg)).join(" "), "frontend");
    };

    console.warn = (...args: unknown[]) => {
      originalConsole.warn(...args);
      addLog("warn", args.map((arg) => String(arg)).join(" "), "frontend");
    };

    console.error = (...args: unknown[]) => {
      originalConsole.error(...args);
      addLog("error", args.map((arg) => String(arg)).join(" "), "frontend");
    };

    console.debug = (...args: unknown[]) => {
      originalConsole.debug(...args);
      addLog("debug", args.map((arg) => String(arg)).join(" "), "frontend");
    };

    // Capture unhandled errors
    window.addEventListener("error", (event) => {
      addLog(
        "error",
        `[Unhandled Error] ${event.message} at ${event.filename}:${event.lineno}:${event.colno}`,
        "frontend"
      );
    });

    // Capture unhandled promise rejections
    window.addEventListener("unhandledrejection", (event) => {
      addLog("error", `[Unhandled Promise Rejection] ${event.reason}`, "frontend");
    });
  }

  function restoreConsole() {
    console.log = originalConsole.log;
    console.info = originalConsole.info;
    console.warn = originalConsole.warn;
    console.error = originalConsole.error;
    console.debug = originalConsole.debug;
  }

  // Listen for backend logs
  async function setupBackendLogsListener() {
    await listen<{ level: LogLevel; message: string }>("debug:log", (event) => {
      addLog(event.payload.level, event.payload.message, "backend");
    });
  }

  return {
    subscribe,
    toggle: () => update((state) => ({ ...state, isOpen: !state.isOpen })),
    open: () => update((state) => ({ ...state, isOpen: true })),
    close: () => update((state) => ({ ...state, isOpen: false })),
    clear: () => update((state) => ({ ...state, logs: [] })),
    setFilterLevel: (level: LogLevel | "all") =>
      update((state) => ({ ...state, filterLevel: level })),
    setAutoScroll: (enabled: boolean) => update((state) => ({ ...state, autoScroll: enabled })),
    setupConsoleCapture,
    restoreConsole,
    setupBackendLogsListener,
  };
}

export const debugConsoleStore = createDebugConsoleStore();

// Derived store for filtered logs
export const filteredLogs = derived(debugConsoleStore, ($debugConsole) => {
  if ($debugConsole.filterLevel === "all") {
    return $debugConsole.logs;
  }

  const levelPriority: Record<LogLevel, number> = {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3,
  };

  const minPriority = levelPriority[$debugConsole.filterLevel];

  return $debugConsole.logs.filter((log) => levelPriority[log.level] >= minPriority);
});
