import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { parseHTML } from 'linkedom';

type SessionsModule = {
  SessionsPage: {
    render: (el: unknown, client: unknown) => Promise<void>;
    teardown: () => void;
  };
};

function installSelectValueShim(windowObj: any) {
  const proto = windowObj?.HTMLSelectElement?.prototype;
  if (!proto) {return;}
  const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');
  if (descriptor?.set) {return;}
  Object.defineProperty(proto, 'value', {
    configurable: true,
    get() {
      const options = Array.from((this as any).options ?? []) as any[];
      const selected = options.find((option: any) => option.selected);
      return selected ? String(selected.value ?? '') : '';
    },
    set(next) {
      const desired = String(next ?? '');
      const options = Array.from((this as any).options ?? []) as any[];
      let matched = false;
      for (const option of options as any[]) {
        const selected = String(option.value ?? '') === desired;
        option.selected = selected;
        if (selected) {
          matched = true;
        }
      }
      if (!matched && options.length > 0) {
        (options[0] as any).selected = true;
      }
    },
  });
}

function createClient() {
  const calls: Array<{ method: string; params?: Record<string, unknown> }> = [];
  const sessions = [
    {
      id: 'ws:alpha',
      frontend: 'ws',
      messageCount: 5,
      config: { modelTier: 'default', queue: { mode: 'collect', cap: 10 } },
      lastMessageAt: Date.now(),
    },
    {
      id: 'telegram:bravo',
      frontend: 'telegram',
      messageCount: 2,
      config: { modelTier: 'fast', queue: { mode: 'interrupt', cap: 5 } },
      lastMessageAt: Date.now() - 1000,
    },
  ];

  const client = {
    async call(method: string, params?: Record<string, unknown>) {
      calls.push({ method, params });
      if (method === 'sessions.list') {
        return { sessions };
      }
      if (method === 'sessions.history') {
        return {
          messages: [
            { role: 'user', content: 'hello' },
            { role: 'assistant', content: 'world' },
          ],
        };
      }
      if (method === 'sessions.delete') {
        return { success: true };
      }
      return null;
    },
  };
  return { client, calls };
}

describe('SessionsPage wiring', () => {
  let root: any;
  let windowObj: any;
  let SessionsPage: SessionsModule['SessionsPage'];

  beforeEach(async () => {
    vi.resetModules();
    const { document, window } = parseHTML('<!doctype html><html><body><div id="root"></div></body></html>') as unknown as {
      document: any;
      window: any;
    };

    (globalThis as any).document = document;
    (globalThis as any).window = window;
    (globalThis as any).confirm = vi.fn(() => true);
    (globalThis as any).alert = vi.fn();

    root = document.getElementById('root');
    windowObj = window;
    installSelectValueShim(windowObj);

    // @ts-expect-error JS module without declaration file.
    const mod = await import('./sessions.js') as unknown as SessionsModule;
    SessionsPage = mod.SessionsPage;
  });

  afterEach(() => {
    SessionsPage.teardown();
    delete (globalThis as any).document;
    delete (globalThis as any).window;
    delete (globalThis as any).confirm;
    delete (globalThis as any).alert;
  });

  it('wires filters, view, delete, and refresh', async () => {
    const { client, calls } = createClient();
    await SessionsPage.render(root, client);

    expect(calls.some((entry) => entry.method === 'sessions.list')).toBe(true);

    const frontend = root.querySelector('#sessions-frontend-filter');
    const telegramOpt = frontend.querySelector('option[value="telegram"]');
    if (telegramOpt) {
      telegramOpt.selected = true;
    }
    frontend.dispatchEvent(new windowObj.Event('change', { bubbles: true }));
    await Promise.resolve();

    const inactive = root.querySelector('#sessions-include-inactive');
    inactive.checked = false;
    inactive.dispatchEvent(new windowObj.Event('change', { bubbles: true }));
    await Promise.resolve();

    const refresh = root.querySelector('#sessions-refresh-btn');
    refresh.dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    await Promise.resolve();

    const viewBtn = root.querySelector('.session-view-btn');
    viewBtn.dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    await Promise.resolve();

    const deleteBtn = root.querySelector('.session-delete-btn');
    deleteBtn.dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    await Promise.resolve();

    const listCalls = calls.filter((entry) => entry.method === 'sessions.list');
    expect(listCalls.length).toBeGreaterThanOrEqual(4);
    expect(listCalls.some((entry) => entry.params?.frontend === 'telegram')).toBe(true);
    expect(listCalls.some((entry) => entry.params?.includePersisted === false)).toBe(true);

    expect(calls.some((entry) => entry.method === 'sessions.history')).toBe(true);
    expect(calls.some((entry) => entry.method === 'sessions.delete')).toBe(true);
    expect((globalThis as any).confirm).toHaveBeenCalled();
  });
});
