import { describe, it, expect } from 'vitest';
import { generate } from '../generate.js';
import type { ScaffoldUiPrimitivesInput } from '../types.js';

function fixture(overrides: Partial<ScaffoldUiPrimitivesInput> = {}): ScaffoldUiPrimitivesInput {
  return { projectPath: '/tmp/web', appCode: 'crm', force: false, ...overrides };
}

const TABLE_PRIMITIVES = [
  'src/components/ui/TruncatedText.tsx',
  'src/components/ui/ResponsiveDataTable.tsx',
];

function fileByPath(path: string): { path: string; content: string; strategy: string } {
  const files = generate(fixture());
  const f = files.find((x) => x.path === path);
  if (!f) throw new Error(`missing generated file ${path}`);
  return f;
}

describe('scaffold-ui-primitives / table primitives — file shape', () => {
  it.each(TABLE_PRIMITIVES)('emits %s with overwrite strategy + AUTO-GENERATED header', (path) => {
    const f = fileByPath(path);
    expect(f.strategy).toBe('overwrite');
    expect(f.content).toMatch(/AUTO-GENERATED/);
    expect(f.content).toMatch(/scaffold-ui-primitives/);
  });
});

describe('scaffold-ui-primitives / table primitives — theme compliance (non-negotiable)', () => {
  const FORBIDDEN_PALETTES = [
    'slate', 'gray', 'zinc', 'neutral', 'stone',
    'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal',
    'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose',
  ];

  it.each(TABLE_PRIMITIVES)('%s uses zero hardcoded Tailwind color classes', (path) => {
    const content = fileByPath(path).content;
    for (const palette of FORBIDDEN_PALETTES) {
      const re = new RegExp(`\\b(?:bg|text|border|ring|placeholder|from|to|via|divide|outline)-${palette}-\\d{2,3}\\b`);
      const matches = content.match(re);
      expect(matches, `Hardcoded Tailwind color (${palette}): ${matches?.join(', ')}`).toBeNull();
    }
  });

  it.each(TABLE_PRIMITIVES)('%s uses zero dark: prefix', (path) => {
    expect(fileByPath(path).content).not.toMatch(/\bdark:/);
  });

  it.each(TABLE_PRIMITIVES)('%s uses zero hex literals in className', (path) => {
    const content = fileByPath(path).content;
    const classNameBlocks = content.match(/className=[`"']([^`"']*)[`"']/g) ?? [];
    for (const block of classNameBlocks) {
      expect(block, `Hex literal in className: ${block}`).not.toMatch(/#[0-9a-fA-F]{3,8}/);
    }
  });
});

describe('scaffold-ui-primitives / TruncatedText — contract', () => {
  const c = () => fileByPath('src/components/ui/TruncatedText.tsx').content;

  it('exports the component and a default export', () => {
    expect(c()).toMatch(/export function TruncatedText\(/);
    expect(c()).toMatch(/export default TruncatedText/);
  });

  it('detects real truncation (scrollWidth > clientWidth) and clips with truncate', () => {
    expect(c()).toMatch(/scrollWidth\s*>\s*el\.clientWidth/);
    expect(c()).toMatch(/\btruncate\b/);
  });

  it('shows the full text via the package Tooltip, gated by disabled when not truncated', () => {
    expect(c()).toMatch(/import \{ Tooltip \} from '@atlashub\/smartstack'/);
    expect(c()).toMatch(/<Tooltip[\s\S]*disabled=\{!truncated/);
  });

  it('accepts a maxWidth prop so the ellipsis triggers without table-layout: fixed', () => {
    expect(c()).toMatch(/maxWidth/);
  });
});

describe('scaffold-ui-primitives / ResponsiveDataTable — contract', () => {
  const c = () => fileByPath('src/components/ui/ResponsiveDataTable.tsx').content;

  it('exports the component, the ResponsiveColumn type and ColumnBreakpoint', () => {
    expect(c()).toMatch(/export function ResponsiveDataTable</);
    expect(c()).toMatch(/export type ResponsiveColumn<T>/);
    expect(c()).toMatch(/export type ColumnBreakpoint = 'sm' \| 'md' \| 'lg' \| 'xl'/);
  });

  it('wraps the LOCAL DataTable (not the package one)', () => {
    expect(c()).toMatch(/from '@\/components\/ui\/DataTable'/);
    expect(c()).toMatch(/<DataTable<T> columns=\{visible\}/);
  });

  it('filters columns by minBreakpoint and always keeps no-minBreakpoint + the actions column', () => {
    expect(c()).toMatch(/BREAKPOINTS[\s\S]*sm:\s*640[\s\S]*md:\s*768[\s\S]*lg:\s*1024[\s\S]*xl:\s*1280/);
    expect(c()).toMatch(/c\.key === ALWAYS_KEY \|\| !c\.minBreakpoint/);
    expect(c()).toMatch(/ALWAYS_KEY = '__actions'/);
  });

  it('clips truncate columns through TruncatedText (resolving nested keys)', () => {
    expect(c()).toMatch(/import \{ TruncatedText \} from '@\/components\/ui\/TruncatedText'/);
    expect(c()).toMatch(/if \(!c\.truncate \|\| c\.render\) return c/);
    expect(c()).toMatch(/<TruncatedText>\{String\(getNestedValue\(item, key\)/);
  });

  it('uses window.innerWidth (jsdom-testable, no matchMedia call)', () => {
    expect(c()).toMatch(/window\.innerWidth/);
    expect(c()).not.toMatch(/matchMedia\s*\(/);
  });
});
