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

vi.mock('../../../shared/state', () => ({
  sendMessage: vi.fn(),
  openVaultTab: vi.fn(),
  registerHost: vi.fn(),
  getState: vi.fn(),
  setState: vi.fn(),
  navigate: vi.fn(),
  escapeHtml: (s: string) => s,
  popOutToTab: vi.fn(),
  isInTab: vi.fn(() => false),
}));

import { sendMessage } from '../../../shared/state';
import { renderImportPanel, teardown } from '../import-panel';

const mockSendMessage = sendMessage as ReturnType<typeof vi.fn>;

function fakeItem(type: 'login' | 'secure_note', title: string) {
  return {
    id: 'fake0000',
    title,
    type,
    tags: [],
    favorite: false,
    created: 0,
    modified: 0,
    core: type === 'login'
      ? { type: 'login', username: 'u', password: 'p' }
      : { type: 'secure_note', body: 'note' },
    sections: [],
    attachments: [],
    field_history: {},
  };
}

async function pickFile(app: HTMLElement, body: string): Promise<void> {
  const file = new File([body], 'export.csv', { type: 'text/csv' });
  const input = app.querySelector('#lp-file') as HTMLInputElement;
  Object.defineProperty(input, 'files', { value: [file] });
  input.dispatchEvent(new Event('change'));
  // Allow microtasks to drain.
  await new Promise((r) => setTimeout(r, 10));
}

describe('Import panel', () => {
  let app: HTMLElement;

  beforeEach(() => {
    mockSendMessage.mockReset();
    teardown();
    document.body.innerHTML = '<div id="app"></div>';
    app = document.getElementById('app')!;
  });

  it('parsing fires parse_lastpass_csv with the file bytes', async () => {
    renderImportPanel(app);
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: { items: [fakeItem('login', 'A')], warnings: [] },
    });

    await pickFile(app, 'url,username,password,totp,extra,name,grouping,fav\nhttps://x,u,p,,,A,,');

    expect(mockSendMessage).toHaveBeenCalledTimes(1);
    expect((mockSendMessage.mock.calls[0][0] as { type: string }).type).toBe('parse_lastpass_csv');
  });

  it('preview text shows parsed counts', async () => {
    renderImportPanel(app);
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: {
        items: [fakeItem('login', 'A'), fakeItem('login', 'B'), fakeItem('secure_note', 'N')],
        warnings: [{ row: 5, title: 'X', message: 'missing `password` — skipped' }],
      },
    });

    await pickFile(app, 'header,row,doesnt,matter,for,this,test,case');

    const txt = (app.querySelector('#lp-preview-text') as HTMLElement).textContent ?? '';
    expect(txt).toContain('2 logins');
    expect(txt).toContain('1 notes');
    expect(txt).toContain('1 skipped');
    expect(txt).toContain('proceed?');
  });

  it('confirm fires import_lastpass_commit with the parsed items', async () => {
    renderImportPanel(app);
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: { items: [fakeItem('login', 'A'), fakeItem('login', 'B')], warnings: [] },
    });
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: { summary: { itemCount: 2 } },
    });

    await pickFile(app, 'header');
    (app.querySelector('#lp-confirm-btn') as HTMLButtonElement).click();
    await new Promise((r) => setTimeout(r, 10));

    expect(mockSendMessage).toHaveBeenCalledTimes(2);
    const second = mockSendMessage.mock.calls[1][0] as {
      type: string;
      items: unknown[];
    };
    expect(second.type).toBe('import_lastpass_commit');
    expect(second.items).toHaveLength(2);
  });

  it('renders warning list after a successful import', async () => {
    renderImportPanel(app);
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: {
        items: [fakeItem('login', 'A')],
        warnings: [
          { row: 4, title: 'AWS', message: 'invalid base32 TOTP secret — login imported without TOTP' },
        ],
      },
    });
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: { summary: { itemCount: 1 } },
    });

    await pickFile(app, 'header');
    (app.querySelector('#lp-confirm-btn') as HTMLButtonElement).click();
    await new Promise((r) => setTimeout(r, 10));

    const list = app.querySelector('#lp-warning-list')?.textContent ?? '';
    expect(list).toContain('row 4');
    expect(list).toContain('AWS');
    expect(list).toContain('TOTP');
  });

  it('cancel clears the preview', async () => {
    renderImportPanel(app);
    mockSendMessage.mockResolvedValueOnce({
      ok: true,
      data: { items: [fakeItem('login', 'A')], warnings: [] },
    });

    await pickFile(app, 'header');
    expect(app.querySelector('#lp-preview')!.classList.contains('hidden')).toBe(false);

    (app.querySelector('#lp-cancel-btn') as HTMLButtonElement).click();
    expect(app.querySelector('#lp-preview')!.classList.contains('hidden')).toBe(true);
  });
});
