/**
 * dev-ui-022.test.ts — FK fields must render as <EntityLookup>, not <input>/<select>.
 *
 * The test creates a tiny project tree under os.tmpdir and runs the audit
 * filtered to a single rule. Symmetric to backend DEV-DAT-008 — see
 * audit-dev-data for the corresponding C# bare-Guid check.
 */
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { audit } from '../audit.js';

function writeFile(root: string, rel: string, content: string): void {
  const abs = path.join(root, rel);
  mkdirSync(path.dirname(abs), { recursive: true });
  writeFileSync(abs, content, 'utf-8');
}

const PAGE_WITH_INPUT_FK = `
import { useState } from 'react';
import { PageTemplate } from '@/components/ui/PageTemplate';

export function EmployeeFormPage() {
  const [formData, setFormData] = useState({ firstName: '', departmentId: '' });
  return (
    <PageTemplate title="New employee">
      <form>
        <input id="firstName" type="text" value={formData.firstName} />
        <input id="departmentId" type="text" value={formData.departmentId} />
      </form>
    </PageTemplate>
  );
}
`;

const PAGE_WITH_SELECT_FK = `
export function EmployeeFormPage() {
  const formData = { departmentId: '' };
  return (
    <select value={formData.departmentId} onChange={() => {}}>
      <option value="">--</option>
    </select>
  );
}
`;

const PAGE_WITH_ENTITY_LOOKUP = `
import { EntityLookup } from '@/components/ui/EntityLookup';

export function EmployeeFormPage() {
  const formData = { departmentId: null as string | null };
  return (
    <EntityLookup
      apiEndpoint="/api/hr/departments/lookup"
      value={formData.departmentId}
      onChange={() => {}}
      label="Department"
      required
    />
  );
}
`;

const PAGE_WITH_WHITELISTED_FIELDS = `
export function PersonFormPage() {
  const formData = { externalId: '', parentId: '', guidId: '' };
  return (
    <form>
      <input id="externalId" type="text" value={formData.externalId} />
      <input name="parentId" type="text" value={formData.parentId} />
      <input id="guidId" type="text" value={formData.guidId} />
    </form>
  );
}
`;

const PAGE_WITH_HIDDEN_FK = `
export function HiddenFormPage() {
  const formData = { auditTrailId: '' };
  return (
    <form>
      <input type="hidden" name="auditTrailId" value={formData.auditTrailId} />
    </form>
  );
}
`;

const LIST_PAGE_WITH_FK_INPUT = `
// List pages are out of scope for DEV-UI-022 — they're not user-editable.
export function EmployeeListPage() {
  return <input id="departmentId" type="text" />;
}
`;

function baseArgs(projectPath: string) {
  return {
    projectPath,
    mode: 'audit' as const,
    moduleCode: 'hr',
    appCode: 'core',
    rules: ['DEV-UI-022'],
    regenerateThreshold: 0,
  };
}

describe('audit-dev-frontend / DEV-UI-022 — FK fields render via <EntityLookup>', () => {
  let root: string;

  beforeEach(() => {
    root = mkdtempSync(path.join(tmpdir(), 'audit-dev-ui-022-'));
  });

  afterEach(() => {
    rmSync(root, { recursive: true, force: true });
  });

  it('flags a Form page that renders a FK as <input name="...Id"> (BLOCKING)', async () => {
    writeFile(root, 'src/pages/hr/employees/EmployeeFormPage.tsx', PAGE_WITH_INPUT_FK);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    expect(errs.length).toBe(1);
    expect(errs[0].params.field).toBe('departmentId');
    expect(errs[0].params.entity).toBe('Employee');
    expect(errs[0].file).toMatch(/EmployeeFormPage\.tsx$/);
    expect(errs[0].solution).toMatch(/EntityLookup/);
    expect(errs[0].solution).toMatch(/scaffold-component/);
  });

  it('flags a Form page that renders a FK as <select value={formData.xxxId}> (BLOCKING)', async () => {
    writeFile(root, 'src/pages/hr/employees/EmployeeFormPage.tsx', PAGE_WITH_SELECT_FK);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    expect(errs.length).toBe(1);
    expect(errs[0].params.field).toBe('departmentId');
  });

  it('passes (ok) when a Form page renders FK via <EntityLookup>', async () => {
    writeFile(root, 'src/pages/hr/employees/EmployeeFormPage.tsx', PAGE_WITH_ENTITY_LOOKUP);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    const oks = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'ok');
    expect(errs.length).toBe(0);
    expect(oks.length).toBeGreaterThan(0);
  });

  it('whitelists non-FK identifiers (externalId, parentId, guidId)', async () => {
    writeFile(root, 'src/pages/hr/persons/PersonFormPage.tsx', PAGE_WITH_WHITELISTED_FIELDS);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    expect(errs.length).toBe(0);
  });

  it('ignores type="hidden" inputs (auth tokens, audit trails — not user-facing)', async () => {
    writeFile(root, 'src/pages/hr/forms/HiddenFormPage.tsx', PAGE_WITH_HIDDEN_FK);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    expect(errs.length).toBe(0);
  });

  it('is scoped to form / create / edit pages — ListPage with FK is out of scope', async () => {
    writeFile(root, 'src/pages/hr/employees/EmployeeListPage.tsx', LIST_PAGE_WITH_FK_INPUT);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    expect(errs.length).toBe(0);
  });

  it('emits one finding PER FK field, not one per page', async () => {
    const TWO_FK_PAGE = `
      export function OrderFormPage() {
        const formData = { clientId: '', salesRepId: '' };
        return (<>
          <input id="clientId" type="text" value={formData.clientId} />
          <input id="salesRepId" type="text" value={formData.salesRepId} />
        </>);
      }
    `;
    writeFile(root, 'src/pages/sales/orders/OrderFormPage.tsx', TWO_FK_PAGE);
    const report = await audit(baseArgs(root));
    const errs = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'err');
    expect(errs.length).toBe(2);
    const fields = errs.map((e) => e.params.field).sort();
    expect(fields).toEqual(['clientId', 'salesRepId']);
  });

  it('returns ok with note when there are no form pages on disk', async () => {
    // No pages written — empty project
    const report = await audit(baseArgs(root));
    const oks = report.findings.filter((f) => f.code === 'DEV-UI-022' && f.severity === 'ok');
    expect(oks.length).toBe(1);
    expect(oks[0].params.note).toBe('no form pages on disk');
  });
});
