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

const mockIsPushSupported = vi.fn(() => true);
const mockGetPushStatus = vi.fn(async () => ({
  supported: true,
  permission: 'granted',
  subscribed: false,
  enabled: true,
  configured: true,
  message: null,
}));
const mockEnablePushNotifications = vi.fn(async () => ({}));
const mockDisablePushNotifications = vi.fn(async () => ({}));

vi.mock('../lib/pwa.js', () => ({
  isPushSupported: mockIsPushSupported,
  getPushStatus: mockGetPushStatus,
  enablePushNotifications: mockEnablePushNotifications,
  disablePushNotifications: mockDisablePushNotifications,
}));

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

function createClient() {
  const calls: Array<{ method: string; params?: Record<string, unknown> }> = [];
  const client = {
    async call(method: string, params?: Record<string, unknown>) {
      calls.push({ method, params });
      if (method === 'config.get') {
        return {
          automation: {
            delivery_mode: 'shared_session',
            daily_briefing: {
              enabled: true,
              output: { channel: 'telegram', peer: '1001' },
            },
          },
          memory: {
            daily_log: { enabled: true },
            proactive_extract: { enabled: true, min_tool_calls: 2 },
          },
          tts: {
            enabled: false,
            enabled_channels: ['telegram'],
          },
          hooks: {
            confirm: ['tool:group:fs/**/*'],
            log: ['tool:web.*'],
            silent: [],
          },
        };
      }
      if (method === 'tools.list') {
        return { tools: [{ name: 'file.read', description: 'Read file' }] };
      }
      if (method === 'system.services') {
        return { services: [{ name: 'telegram', type: 'channel', status: 'connected', description: 'Telegram bot' }] };
      }
      if (method === 'config.patch') {
        return { applied: Object.keys((params?.patches ?? {}) as Record<string, unknown>), rejected: [], persisted: true };
      }
      return null;
    },
  };
  return { client, calls };
}

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

  beforeEach(async () => {
    vi.resetModules();
    mockIsPushSupported.mockClear();
    mockGetPushStatus.mockClear();
    mockEnablePushNotifications.mockClear();
    mockDisablePushNotifications.mockClear();

    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;
    root = document.getElementById('root');
    windowObj = window;

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

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

  it('wires assistant mode + hook + push actions', async () => {
    const { client, calls } = createClient();
    await SettingsPage.render(root, client);

    expect(root.querySelector('#assistant-mode-save')).toBeTruthy();
    expect(root.querySelector('#hooks-save')).toBeTruthy();
    expect(root.querySelector('#push-enable')).toBeTruthy();
    expect(root.querySelector('#push-disable')).toBeTruthy();

    root.querySelector('#assist-delivery-announce').checked = true;
    root.querySelector('#assist-daily-briefing').checked = false;
    root.querySelector('#assist-memory-daily').checked = false;
    root.querySelector('#assist-memory-proactive').checked = true;
    root.querySelector('#assist-memory-min-tools').value = '6';
    root.querySelector('#assist-tts-enabled').checked = true;
    root.querySelector('#assist-tts-channels').value = 'telegram, discord';
    root.querySelector('#assist-briefing-channel').value = 'discord';
    root.querySelector('#assist-briefing-peer').value = '98765';

    root.querySelector('#assistant-mode-save').dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    await Promise.resolve();

    const assistantPatch = calls.find((entry) => entry.method === 'config.patch' && Object.prototype.hasOwnProperty.call(entry.params?.patches ?? {}, 'automation.delivery_mode'));
    expect(assistantPatch).toBeTruthy();

    root.querySelector('#hooks-confirm').value = 'tool:group:fs/**/*\ntool:group:web/**/*';
    root.querySelector('#hooks-log').value = 'tool:web.search';
    root.querySelector('#hooks-silent').value = 'tool:cron.trigger';

    root.querySelector('#hooks-save').dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    await Promise.resolve();

    const hookPatch = calls.find((entry) => entry.method === 'config.patch' && Object.prototype.hasOwnProperty.call(entry.params?.patches ?? {}, 'hooks.confirm'));
    expect(hookPatch).toBeTruthy();

    root.querySelector('#push-enable').dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    root.querySelector('#push-disable').dispatchEvent(new windowObj.Event('click', { bubbles: true }));
    await Promise.resolve();

    expect(mockEnablePushNotifications).toHaveBeenCalledTimes(1);
    expect(mockDisablePushNotifications).toHaveBeenCalledTimes(1);
    expect(mockGetPushStatus).toHaveBeenCalled();
  });
});
