import "@testing-library/jest-dom/vitest";
import { vi, beforeEach } from "vitest";

// Mock Tauri invoke API
const mockInvokeResults: Record<string, unknown> = {};

export function setMockInvokeResult(command: string, result: unknown) {
  mockInvokeResults[command] = result;
}

export function clearMockInvokeResults() {
  Object.keys(mockInvokeResults).forEach((key) => delete mockInvokeResults[key]);
}

vi.mock("@tauri-apps/api/core", () => ({
  invoke: vi.fn((command: string) => {
    if (command in mockInvokeResults) {
      const result = mockInvokeResults[command];
      if (result instanceof Error) {
        return Promise.reject(result);
      }
      return Promise.resolve(result);
    }
    // Default return values for common commands
    switch (command) {
      case "get_config":
        return Promise.resolve({
          model: null,
          api_key: null,
          custom_instructions: null,
          mcp_servers_json: null,
          auto_granted_tools: [],
          theme: "dark",
          greeting_enabled: true,
          greeting_custom_prompt: null,
          notifications_enabled: true,
          notification_volume: 0.7,
          always_on_top: false,
          update_checks_enabled: true,
          character_panel_width: null,
          font_size: 14,
          minimize_to_tray: false,
          streamer_mode: false,
          streamer_hide_paths: false,
          compact_mode: false,
          profile_name: null,
          profile_avatar_path: null,
          profile_bio: null,
          custom_theme_colors: {},
        });
      case "list_quick_actions":
        return Promise.resolve([]);
      case "list_snippets":
        return Promise.resolve([]);
      case "list_sessions":
        return Promise.resolve([]);
      case "get_usage_stats":
        return Promise.resolve({
          total_messages: 0,
          total_sessions: 0,
          total_tokens: 0,
          total_cost: 0,
        });
      case "get_persisted_stats":
        return Promise.resolve({
          lifetime_messages: 0,
          lifetime_sessions: 0,
          lifetime_tokens: 0,
          lifetime_cost: 0,
          achievements: [],
          unlocked_achievements: [],
        });
      case "load_saved_achievements":
        return Promise.resolve([]);
      case "list_clipboard_entries":
        return Promise.resolve([]);
      case "cleanup_temp_files":
        return Promise.resolve();
      case "validate_directory":
        return Promise.resolve(true);
      case "git_status":
        return Promise.resolve({
          branch: "main",
          files: [],
          ahead: 0,
          behind: 0,
        });
      default:
        return Promise.resolve(null);
    }
  }),
}));

// Mock Tauri event API
const eventListeners: Map<string, Array<(event: { payload: unknown }) => void>> = new Map();

export function emitMockEvent(eventName: string, payload: unknown) {
  const listeners = eventListeners.get(eventName) || [];
  listeners.forEach((listener) => listener({ payload }));
}

export function clearMockEventListeners() {
  eventListeners.clear();
}

vi.mock("@tauri-apps/api/event", () => ({
  listen: vi.fn((eventName: string, handler: (event: { payload: unknown }) => void) => {
    const listeners = eventListeners.get(eventName) || [];
    listeners.push(handler);
    eventListeners.set(eventName, listeners);
    // Return an unlisten function
    return Promise.resolve(() => {
      const currentListeners = eventListeners.get(eventName) || [];
      const index = currentListeners.indexOf(handler);
      if (index > -1) {
        currentListeners.splice(index, 1);
      }
    });
  }),
  emit: vi.fn(),
}));

// Mock Tauri plugins
vi.mock("@tauri-apps/plugin-dialog", () => ({
  save: vi.fn(() => Promise.resolve(null)),
  open: vi.fn(() => Promise.resolve(null)),
  message: vi.fn(() => Promise.resolve()),
  ask: vi.fn(() => Promise.resolve(true)),
  confirm: vi.fn(() => Promise.resolve(true)),
}));

vi.mock("@tauri-apps/plugin-fs", () => ({
  writeTextFile: vi.fn(() => Promise.resolve()),
  readTextFile: vi.fn(() => Promise.resolve("{}")),
  exists: vi.fn(() => Promise.resolve(false)),
  mkdir: vi.fn(() => Promise.resolve()),
  remove: vi.fn(() => Promise.resolve()),
  readDir: vi.fn(() => Promise.resolve([])),
}));

vi.mock("@tauri-apps/plugin-opener", () => ({
  openPath: vi.fn(() => Promise.resolve()),
  openUrl: vi.fn(() => Promise.resolve()),
}));

vi.mock("@tauri-apps/plugin-notification", () => ({
  sendNotification: vi.fn(() => Promise.resolve()),
  requestPermission: vi.fn(() => Promise.resolve("granted")),
  isPermissionGranted: vi.fn(() => Promise.resolve(true)),
}));

vi.mock("@tauri-apps/plugin-clipboard-manager", () => ({
  writeText: vi.fn(() => Promise.resolve()),
  readText: vi.fn(() => Promise.resolve("")),
  writeImage: vi.fn(() => Promise.resolve()),
  readImage: vi.fn(() => Promise.resolve(null)),
}));

vi.mock("@tauri-apps/plugin-os", () => ({
  platform: vi.fn(() => Promise.resolve("linux")),
  arch: vi.fn(() => Promise.resolve("x86_64")),
  type: vi.fn(() => Promise.resolve("Linux")),
  version: vi.fn(() => Promise.resolve("1.0.0")),
}));

vi.mock("@tauri-apps/plugin-http", () => ({
  fetch: vi.fn(() =>
    Promise.resolve({
      ok: true,
      status: 200,
      json: () => Promise.resolve({}),
      text: () => Promise.resolve(""),
    })
  ),
}));

// Mock browser APIs
class MockAudioElement {
  src = "";
  volume = 1;
  loop = false;
  currentTime = 0;
  paused = true;
  preload = "auto";
  onloadeddata: (() => void) | null = null;
  onended: (() => void) | null = null;
  onerror: ((e: Event) => void) | null = null;

  play() {
    this.paused = false;
    return Promise.resolve();
  }

  pause() {
    this.paused = true;
  }

  load() {
    if (this.onloadeddata) {
      setTimeout(() => this.onloadeddata?.(), 0);
    }
  }

  addEventListener(event: string, handler: () => void) {
    if (event === "loadeddata") this.onloadeddata = handler;
    if (event === "ended") this.onended = handler;
  }

  removeEventListener() {
    // No-op for tests
  }
}

// @ts-expect-error - Mock Audio constructor
globalThis.Audio = MockAudioElement;

// Mock localStorage
const localStorageStore: Record<string, string> = {};

Object.defineProperty(globalThis, "localStorage", {
  value: {
    getItem: vi.fn((key: string) => localStorageStore[key] || null),
    setItem: vi.fn((key: string, value: string) => {
      localStorageStore[key] = value;
    }),
    removeItem: vi.fn((key: string) => {
      delete localStorageStore[key];
    }),
    clear: vi.fn(() => {
      Object.keys(localStorageStore).forEach((key) => delete localStorageStore[key]);
    }),
    key: vi.fn((index: number) => Object.keys(localStorageStore)[index] || null),
    get length() {
      return Object.keys(localStorageStore).length;
    },
  },
  writable: true,
});

// Mock matchMedia
Object.defineProperty(globalThis, "matchMedia", {
  writable: true,
  value: vi.fn((query: string) => ({
    matches: query.includes("dark"),
    media: query,
    onchange: null,
    addListener: vi.fn(),
    removeListener: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn(),
  })),
});

// Mock ResizeObserver
class MockResizeObserver {
  observe = vi.fn();
  unobserve = vi.fn();
  disconnect = vi.fn();
}

globalThis.ResizeObserver = MockResizeObserver as unknown as typeof ResizeObserver;

// Mock IntersectionObserver
class MockIntersectionObserver {
  observe = vi.fn();
  unobserve = vi.fn();
  disconnect = vi.fn();
}

globalThis.IntersectionObserver =
  MockIntersectionObserver as unknown as typeof IntersectionObserver;

// Mock requestAnimationFrame
globalThis.requestAnimationFrame = vi.fn((callback) => {
  return setTimeout(callback, 0) as unknown as number;
});

globalThis.cancelAnimationFrame = vi.fn((id) => {
  clearTimeout(id);
});

// Reset all mocks before each test
beforeEach(() => {
  vi.clearAllMocks();
  clearMockInvokeResults();
  clearMockEventListeners();
});
