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: 'add', entries: [], selectedId: null, selectedItem: null, selectedIndex: 0,
    searchQuery: '', activeGroup: null, error: null, loading: false,
    capturedTabId: null, capturedUrl: '', newType: 'login',
    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(),
  };
});

// Mock setup-helpers (scheduleRate used by wirePasswordStrength)
vi.mock('../../../../setup/setup-helpers', () => ({
  scheduleRate: vi.fn(),
  STRENGTH_LABELS: {},
  entropyText: vi.fn(() => ''),
}));

import { renderForm, applyGeneratedPassword } from '../login';
import { sendMessage } from '../../../../shared/state';

describe('login form smart inputs', () => {
  beforeEach(() => {
    document.body.innerHTML = '<div id="app"></div>';
    // chrome.storage.local stub (needed by wireNotesMonoToggle)
    (globalThis as any).chrome = {
      storage: {
        local: {
          get: vi.fn().mockImplementation((_keys: any, cb: any) => cb({})),
          set: vi.fn().mockImplementation((_obj: any, cb: any) => cb && cb()),
        },
      },
      runtime: {
        sendMessage: vi.fn(),
      },
    };
    vi.mocked(sendMessage).mockReset();
    vi.mocked(sendMessage).mockResolvedValue({ ok: true, data: { groups: [] } });
  });

  it('renders all 6 smart-input slots in the form', async () => {
    const app = document.getElementById('app')!;
    renderForm(app, 'add', null);

    expect(document.querySelector('#fill-from-tab-btn')).not.toBeNull();
    expect(document.querySelector('#hostname-chip-row')).not.toBeNull();
    expect(document.querySelector('#reveal-password-btn')).not.toBeNull();
    expect(document.querySelector('#strength-bar-row')).not.toBeNull();
    expect(document.querySelector('#totp-preview-row')).not.toBeNull();
    expect(document.querySelector('#totp-qr-btn')).not.toBeNull();
    expect(document.querySelector('#totp-qr-panel')).not.toBeNull();
    expect(document.querySelector('#notes-mono-btn')).not.toBeNull();
  });
});

describe('renderForm surface flag', () => {
  let app: HTMLElement;
  beforeEach(() => {
    document.body.innerHTML = '<div id="app"></div>';
    app = document.getElementById('app')!;
    (globalThis as any).chrome = {
      storage: {
        local: {
          get: vi.fn().mockImplementation((_keys: any, cb: any) => cb({})),
          set: vi.fn().mockImplementation((_obj: any, cb: any) => cb && cb()),
        },
      },
      runtime: {
        sendMessage: vi.fn(),
      },
    };
    vi.mocked(sendMessage).mockReset();
    vi.mocked(sendMessage).mockResolvedValue({ ok: true, data: { groups: [] } });
  });

  it('renders single-column when surface is "popup" (default)', () => {
    renderForm(app, 'add', null);
    expect(app.querySelector('.form-grid')).toBeNull();
  });

  it('renders two-column .form-grid wrapper when surface is "fullscreen"', () => {
    renderForm(app, 'add', null, { surface: 'fullscreen' });
    const grid = app.querySelector('.form-grid');
    expect(grid).toBeTruthy();
    expect(grid!.querySelector('[data-form-section="identity"]')).toBeTruthy();
    expect(grid!.querySelector('[data-form-section="credentials"]')).toBeTruthy();
  });
});

describe('Login save shape', () => {
  beforeEach(() => {
    document.body.innerHTML = '<div id="app"></div>';
    (globalThis as any).chrome = {
      storage: {
        local: {
          get: vi.fn().mockImplementation((_keys: any, cb: any) => cb({})),
          set: vi.fn().mockImplementation((_obj: any, cb: any) => cb && cb()),
        },
      },
      runtime: { sendMessage: vi.fn() },
    };
    vi.mocked(sendMessage).mockReset();
    vi.mocked(sendMessage).mockImplementation(async (msg: any) => {
      if (msg.type === 'list_groups') return { ok: true, data: { groups: [] } };
      if (msg.type === 'preview_totp_from_secret') return { ok: false };
      return { ok: true, data: { id: 'fakeid0000000000', items: [] } };
    });
  });

  it('saves a login item with url, username, and password', async () => {
    const app = document.getElementById('app')!;
    renderForm(app, 'add', null);

    (document.getElementById('f-title') as HTMLInputElement).value = 'GitHub';
    (document.getElementById('f-url') as HTMLInputElement).value = 'https://github.com/login';
    (document.getElementById('f-username') as HTMLInputElement).value = 'alice';
    (document.getElementById('f-password') as HTMLInputElement).value = 'hunter2';

    document.getElementById('save-btn')!.click();
    await new Promise(r => setTimeout(r, 5));

    const calls = vi.mocked(sendMessage).mock.calls;
    const addCall = calls.find(([msg]) => (msg as { type: string }).type === 'add_item');
    expect(addCall).toBeDefined();
    const msg = addCall![0] as { type: 'add_item'; item: any };
    expect(msg.item.type).toBe('login');
    expect(msg.item.core).toMatchObject({
      type: 'login',
      username: 'alice',
      password: 'hunter2',
      url: 'https://github.com/login',
    });
  });

  it('rejects save when title is empty', async () => {
    const app = document.getElementById('app')!;
    renderForm(app, 'add', null);

    document.getElementById('save-btn')!.click();
    await new Promise(r => setTimeout(r, 5));

    const calls = vi.mocked(sendMessage).mock.calls;
    const addCall = calls.find(([msg]) => (msg as { type: string }).type === 'add_item');
    expect(addCall).toBeUndefined();
  });
});

describe('regenerate handler dispatches input event', () => {
  it('dispatches an InputEvent on the input after value is set', () => {
    const input = document.createElement('input');
    input.type = 'password';
    document.body.appendChild(input);

    const dispatchSpy = vi.spyOn(input, 'dispatchEvent');

    applyGeneratedPassword(input, 'sCMtTJkF%GN^mF#-N6D%');

    expect(input.value).toBe('sCMtTJkF%GN^mF#-N6D%');
    expect(input.type).toBe('text');
    expect(dispatchSpy).toHaveBeenCalled();
    const evt = dispatchSpy.mock.calls.find(c => c[0] instanceof InputEvent)?.[0] as InputEvent;
    expect(evt).toBeDefined();
    expect(evt.type).toBe('input');
    expect(evt.bubbles).toBe(true);

    document.body.removeChild(input);
  });

  it('bubbling listener fires when applyGeneratedPassword is called', () => {
    const input = document.createElement('input');
    document.body.appendChild(input);

    let listenerFired = false;
    input.addEventListener('input', () => { listenerFired = true; });
    applyGeneratedPassword(input, 'newpass');
    expect(listenerFired).toBe(true);

    document.body.removeChild(input);
  });
});
