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

vi.mock('../../../shared/state', async () => {
  const navigate = vi.fn();
  const setState = vi.fn();
  const sendMessage = vi.fn();
  const getState = vi.fn(() => ({
    view: 'settings-vault',
    entries: [], selectedId: null, selectedItem: null, selectedIndex: 0,
    searchQuery: '', activeGroup: null, error: null, loading: false,
    capturedTabId: null, capturedUrl: '', newType: null,
    vaultSettings: {
      trash_retention: { kind: 'days', value: 30 },
      field_history_retention: { kind: 'forever' },
      generator_defaults: {
        kind: 'random', length: 20,
        classes: { lower: true, upper: true, digits: true, symbols: true },
        symbol_charset: { kind: 'safe_only' },
      },
      attachment_caps: {},
      autofill_origin_acks: { 'github.com': 1000000000, 'example.com': 999000000 },
    },
    generatorDefaults: null,
  }));
  const escapeHtml = (s: string) => s
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
  return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() };
});

vi.mock('../generator-panel', () => ({
  openGeneratorPanel: vi.fn(),
  closeGeneratorPanel: vi.fn(),
}));

import { renderVaultSettings } from '../settings-vault';
import { sendMessage } from '../../../shared/state';

describe('settings-vault', () => {
  beforeEach(() => {
    document.body.innerHTML = '<div id="app"></div>';
    vi.mocked(sendMessage).mockReset();
    // Default: get_session_config returns inactivity/15, everything else ok
    vi.mocked(sendMessage).mockImplementation(async (msg: any) => {
      if (msg.type === 'get_session_config') {
        return { ok: true, data: { config: { mode: 'inactivity', minutes: 15 } } };
      }
      return { ok: true };
    });
  });

  it('renders with seeded vault-settings values', async () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    // Initial synchronous render paints the vault settings section headers
    expect(app.querySelector('.section-header')?.textContent).toContain('VAULT SETTINGS');
    expect(app.textContent).toContain('github.com');
    expect(app.textContent).toContain('example.com');
    const trashSel = document.getElementById('trash-retention') as HTMLSelectElement;
    expect(trashSel.value).toBe('days:30');
    const histSel = document.getElementById('history-retention') as HTMLSelectElement;
    expect(histSel.value).toBe('forever');
    // After get_session_config resolves, SESSION row appears
    await new Promise((r) => setTimeout(r, 10));
    expect(app.textContent).toContain('SESSION');
    expect(app.textContent).toContain('after inactivity');
  });

  it('renders origin acks sorted by recency (descending)', () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    const rows = Array.from(document.querySelectorAll('.ack-row__host')).map((e) => e.textContent);
    expect(rows).toEqual(['github.com', 'example.com']);
  });

  it('save button disabled until a change is made', () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    const saveBtn = document.getElementById('save-btn') as HTMLButtonElement;
    expect(saveBtn.disabled).toBe(true);
    const trashSel = document.getElementById('trash-retention') as HTMLSelectElement;
    trashSel.value = 'forever';
    trashSel.dispatchEvent(new Event('change', { bubbles: true }));
    expect((document.getElementById('save-btn') as HTMLButtonElement).disabled).toBe(false);
  });

  it('revoke button removes origin from pending and enables save', () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    (document.querySelector('[data-revoke="github.com"]') as HTMLButtonElement).click();
    expect(document.querySelector('[data-revoke="github.com"]')).toBeNull();
    expect((document.getElementById('save-btn') as HTMLButtonElement).disabled).toBe(false);
  });

  it('save button triggers update_vault_settings with pending', async () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    (document.querySelector('[data-revoke="github.com"]') as HTMLButtonElement).click();
    (document.getElementById('save-btn') as HTMLButtonElement).click();
    await new Promise((r) => setTimeout(r, 10));
    const call = vi.mocked(sendMessage).mock.calls.find(
      ([m]) => (m as any).type === 'update_vault_settings',
    );
    expect(call).toBeDefined();
    const payload = call![0] as { settings: any };
    expect(payload.settings.autofill_origin_acks).not.toHaveProperty('github.com');
    expect(payload.settings.autofill_origin_acks).toHaveProperty('example.com');
  });

  it('section headers render in correct order: VAULT SETTINGS, THIS DEVICE, ACTIONS', () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    const headers = Array.from(document.querySelectorAll('.section-header')).map((e) => e.textContent?.trim());
    expect(headers[0]).toContain('VAULT SETTINGS');
    expect(headers[1]).toContain('THIS DEVICE');
    expect(headers[2]).toContain('ACTIONS');
  });

  it('subtitle shows "no changes" initially', () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    expect(app.querySelector('.settings-header__sub')?.textContent).toBe('no changes');
  });

  it('subtitle shows "unsaved · esc to cancel" after making a change', () => {
    const app = document.getElementById('app')!;
    renderVaultSettings(app);
    const trashSel = document.getElementById('trash-retention') as HTMLSelectElement;
    trashSel.value = 'forever';
    trashSel.dispatchEvent(new Event('change', { bubbles: true }));
    expect(document.querySelector('.settings-header__sub')?.textContent).toContain('unsaved');
  });
});
