/**
 * scaffold-dashboard-primitives/generate.ts — Build the editable dashboard
 * primitives + i18n locale stubs. Pure function (no I/O).
 *
 * Theme-compliance contract: every primitive uses ONLY SmartStack tokens
 * (var(--bg-card), var(--dataviz-*), var(--kpi-*), var(--chart-*), …) — zero
 * hardcoded Tailwind color classes. Charts read the categorical palette from
 * the CSS vars at runtime via useDatavizPalette (Recharts needs real color
 * strings in SVG fills, not var() refs). The companion test asserts this.
 *
 * The package's own dashboard components (KpiCard/ChartContainer/~25 Recharts
 * comps) are INTERNAL and not exported — so, like scaffold-ui-primitives, we
 * emit local editable equivalents the developer owns.
 */
import type { GeneratedFile, ScaffoldDashboardPrimitivesInput } from './types.js';

const HEADER = `/* AUTO-GENERATED — re-run scaffold-dashboard-primitives to refresh. Add @customised at the top to opt out. */`;

const LOCALES = ['fr', 'en', 'it', 'de'] as const;

const I18N_STRINGS: Record<typeof LOCALES[number], Record<string, Record<string, string>>> = {
  fr: { dashboard: { loading: 'Chargement…', error: 'Erreur de chargement', empty: 'Rien à afficher', noData: '—', unknownWidget: 'Widget non supporté' } },
  en: { dashboard: { loading: 'Loading…', error: 'Failed to load', empty: 'Nothing to show', noData: '—', unknownWidget: 'Unsupported widget' } },
  it: { dashboard: { loading: 'Caricamento…', error: 'Errore di caricamento', empty: 'Niente da mostrare', noData: '—', unknownWidget: 'Widget non supportato' } },
  de: { dashboard: { loading: 'Wird geladen…', error: 'Ladefehler', empty: 'Nichts anzuzeigen', noData: '—', unknownWidget: 'Nicht unterstütztes Widget' } },
};

/** Shared TS contract — the single source of truth for widget config + data,
 *  imported by the primitives AND by the generated DashboardPage. */
function typesModule(): string {
  return `${HEADER}
export type DashboardWidgetType = 'kpi' | 'counter' | 'chart-line' | 'chart-bar' | 'chart-pie' | 'list';

export interface DashboardWidget {
  /** Stable key — also the data key the page maps a result onto. */
  key: string;
  /** Human label (already translated by the page). */
  label: string;
  type: DashboardWidgetType;
  /** 1-12 grid span. Defaults per type (kpi 3, charts 6, list 12). */
  col?: number;
  /** Data source path relative to the dashboard endpoint, e.g. 'widgets/active'. */
  endpoint?: string;
  /** Permission gating the widget's visibility. */
  permission?: string;
  /** BA-declared hints (informational — the backend owns the aggregation math). */
  entity?: string;
  aggregation?: 'count' | 'sum' | 'avg' | 'min' | 'max';
  field?: string;
}

/** A point on a line/bar chart: a label + one or more numeric series. */
export interface ChartPoint {
  label: string;
  [series: string]: number | string;
}

/** A slice of a pie chart. */
export interface ChartSlice {
  name: string;
  value: number;
}

export interface ListColumn {
  key: string;
  label: string;
}

/** The data a single widget renders. The backend returns one of these per
 *  widget key (kpi → value/delta, chart-line/bar → points, chart-pie → slices,
 *  list → rows/columns). Absent fields render the muted "no data" placeholder. */
export interface WidgetResult {
  value?: number | string;
  delta?: number;
  unit?: string;
  points?: ChartPoint[];
  slices?: ChartSlice[];
  rows?: Record<string, unknown>[];
  columns?: ListColumn[];
}
`;
}

/** Reads --dataviz-1..8 from the document root so charts follow the theme
 *  (and re-reads on a .dark class toggle). */
function useDatavizPaletteModule(): string {
  return `${HEADER}
import { useEffect, useState } from 'react';

// Last-resort fallback only — the real colors come from the theme's --dataviz-*
// CSS vars (scaffold-theme). Used when the tokens can't be read (e.g. SSR).
const FALLBACK = ['#3b82f6', '#8b5cf6', '#ec4899', '#f59e0b', '#10b981', '#06b6d4', '#ef4444', '#84cc16'];

export function useDatavizPalette(): string[] {
  const [palette, setPalette] = useState<string[]>(FALLBACK);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const read = () => {
      const cs = getComputedStyle(document.documentElement);
      const out: string[] = [];
      for (let i = 1; i <= 8; i++) {
        const v = cs.getPropertyValue('--dataviz-' + i).trim();
        if (v) out.push(v);
      }
      if (out.length) setPalette(out);
    };
    read();
    const obs = new MutationObserver(read);
    obs.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
    return () => obs.disconnect();
  }, []);
  return palette;
}
`;
}

function kpiCardComponent(): string {
  return `${HEADER}
import { useTranslation } from 'react-i18next';
import { ArrowDown, ArrowUp } from 'lucide-react';
import type { DashboardWidget, WidgetResult } from './types';

export interface KpiCardProps {
  widget: DashboardWidget;
  data?: WidgetResult;
  isLoading?: boolean;
  error?: boolean;
}

export function KpiCard({ widget, data, isLoading, error }: KpiCardProps) {
  const { t } = useTranslation('common');
  const value = data?.value;
  const delta = data?.delta;
  const hasValue = value !== undefined && value !== null;
  const showTrend = widget.type === 'counter' && typeof delta === 'number' && delta !== 0;
  const up = (delta ?? 0) > 0;

  return (
    <div className="rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] p-4">
      <div className="text-xs font-semibold uppercase tracking-wider text-[var(--kpi-label)]">{widget.label}</div>
      {isLoading ? (
        <div className="mt-2 h-7 w-20 animate-pulse rounded bg-[var(--bg-muted)]" />
      ) : error || !hasValue ? (
        <div className="mt-1 text-sm text-[var(--text-muted)]">{t('dashboard.noData', { defaultValue: '—' })}</div>
      ) : (
        <div className="mt-1 flex items-baseline gap-2">
          <span className="text-2xl font-semibold text-[var(--kpi-value)]">{String(value)}</span>
          {data?.unit ? <span className="text-base font-normal text-[var(--text-muted)]">{data.unit}</span> : null}
          {showTrend ? (
            <span className="inline-flex items-center gap-0.5 text-xs font-medium" style={{ color: up ? 'var(--kpi-trend-up)' : 'var(--kpi-trend-down)' }}>
              {up ? <ArrowUp className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />}
              {Math.abs(delta as number)}%
            </span>
          ) : null}
        </div>
      )}
    </div>
  );
}
`;
}

function chartCardComponent(): string {
  return `${HEADER}
import { useTranslation } from 'react-i18next';
import {
  ResponsiveContainer, LineChart, Line, BarChart, Bar, PieChart, Pie, Cell,
  XAxis, YAxis, CartesianGrid, Tooltip, Legend,
} from 'recharts';
import type { DashboardWidget, WidgetResult } from './types';
import { useDatavizPalette } from './useDatavizPalette';

export interface ChartCardProps {
  widget: DashboardWidget;
  data?: WidgetResult;
  isLoading?: boolean;
  error?: boolean;
}

export function ChartCard({ widget, data, isLoading, error }: ChartCardProps) {
  const { t } = useTranslation('common');
  const palette = useDatavizPalette();
  const variant = widget.type === 'chart-bar' ? 'bar' : widget.type === 'chart-pie' ? 'pie' : 'line';
  const points = data?.points ?? [];
  const slices = data?.slices ?? [];
  const seriesKeys = points.length ? Object.keys(points[0]).filter((k) => k !== 'label') : [];
  const empty = variant === 'pie' ? slices.length === 0 : points.length === 0;

  const axisProps = { stroke: 'var(--chart-axis)', fontSize: 12, tickLine: false };
  const tooltipStyle = {
    backgroundColor: 'var(--chart-tooltip-bg)',
    color: 'var(--chart-tooltip-text)',
    border: '1px solid var(--border-color)',
    borderRadius: 'var(--radius-card)',
  };

  return (
    <div className="rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] p-4">
      <div className="mb-3 text-sm font-semibold text-[var(--text-primary)]">{widget.label}</div>
      {isLoading ? (
        <div className="h-56 animate-pulse rounded bg-[var(--bg-muted)]" />
      ) : error || empty ? (
        <div className="flex h-56 items-center justify-center text-sm text-[var(--text-muted)]">{t('dashboard.noData', { defaultValue: 'No data' })}</div>
      ) : (
        <div className="h-56">
          <ResponsiveContainer width="100%" height="100%">
            {variant === 'pie' ? (
              <PieChart>
                <Pie data={slices} dataKey="value" nameKey="name" cx="50%" cy="50%" outerRadius="80%">
                  {slices.map((_, i) => <Cell key={i} fill={palette[i % palette.length]} />)}
                </Pie>
                <Tooltip contentStyle={tooltipStyle} />
                <Legend />
              </PieChart>
            ) : variant === 'bar' ? (
              <BarChart data={points}>
                <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" vertical={false} />
                <XAxis dataKey="label" {...axisProps} />
                <YAxis {...axisProps} />
                <Tooltip contentStyle={tooltipStyle} cursor={{ fill: 'var(--bg-muted)' }} />
                {seriesKeys.length > 1 ? <Legend /> : null}
                {seriesKeys.map((k, i) => <Bar key={k} dataKey={k} fill={palette[i % palette.length]} radius={[4, 4, 0, 0]} />)}
              </BarChart>
            ) : (
              <LineChart data={points}>
                <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" vertical={false} />
                <XAxis dataKey="label" {...axisProps} />
                <YAxis {...axisProps} />
                <Tooltip contentStyle={tooltipStyle} />
                {seriesKeys.length > 1 ? <Legend /> : null}
                {seriesKeys.map((k, i) => <Line key={k} type="monotone" dataKey={k} stroke={palette[i % palette.length]} strokeWidth={2} dot={false} />)}
              </LineChart>
            )}
          </ResponsiveContainer>
        </div>
      )}
    </div>
  );
}
`;
}

function listWidgetComponent(): string {
  return `${HEADER}
import { useTranslation } from 'react-i18next';
import type { DashboardWidget, WidgetResult } from './types';

export interface ListWidgetProps {
  widget: DashboardWidget;
  data?: WidgetResult;
  isLoading?: boolean;
  error?: boolean;
}

export function ListWidget({ widget, data, isLoading, error }: ListWidgetProps) {
  const { t } = useTranslation('common');
  const rows = data?.rows ?? [];
  const columns = data?.columns ?? (rows.length ? Object.keys(rows[0]).map((k) => ({ key: k, label: k })) : []);

  return (
    <div className="rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] p-4">
      <div className="mb-3 text-sm font-semibold text-[var(--text-primary)]">{widget.label}</div>
      {isLoading ? (
        <div className="space-y-2">{[0, 1, 2].map((i) => <div key={i} className="h-6 animate-pulse rounded bg-[var(--bg-muted)]" />)}</div>
      ) : error || rows.length === 0 ? (
        <div className="py-6 text-center text-sm text-[var(--text-muted)]">{t('dashboard.empty', { defaultValue: 'Nothing to show' })}</div>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead>
              <tr className="border-b border-[var(--border-color)] text-left text-[var(--text-muted)]">
                {columns.map((c) => <th key={c.key} className="px-2 py-1.5 font-medium">{c.label}</th>)}
              </tr>
            </thead>
            <tbody>
              {rows.map((row, ri) => (
                <tr key={ri} className="border-b border-[var(--border-color)] text-[var(--text-primary)]">
                  {columns.map((c) => <td key={c.key} className="px-2 py-1.5">{String(row[c.key] ?? '')}</td>)}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}
`;
}

function dashboardGridComponent(): string {
  return `${HEADER}
import type { ReactNode } from 'react';

export interface DashboardGridProps {
  children: ReactNode;
}

/**
 * 12-column responsive grid. Each child sets its own span (WidgetRenderer does
 * this from widget.col); on small screens the grid collapses to a single column
 * so widgets stack full-width.
 */
export function DashboardGrid({ children }: DashboardGridProps) {
  return <div className="grid grid-cols-1 gap-4 lg:grid-cols-12">{children}</div>;
}
`;
}

function widgetRendererComponent(): string {
  return `${HEADER}
import { useTranslation } from 'react-i18next';
import type { ReactNode } from 'react';
import type { DashboardWidget, WidgetResult } from './types';
import { KpiCard } from './KpiCard';
import { ChartCard } from './ChartCard';
import { ListWidget } from './ListWidget';

export interface WidgetRendererProps {
  widget: DashboardWidget;
  data?: WidgetResult;
  isLoading?: boolean;
  error?: boolean;
}

const DEFAULT_COL: Record<string, number> = {
  kpi: 3, counter: 3, 'chart-line': 6, 'chart-bar': 6, 'chart-pie': 6, list: 12,
};

/**
 * Renders ONE typed widget. The page owns the data fetch and passes
 * data[widget.key]; an unknown type or absent data degrades to a muted
 * placeholder (never throws). The grid span comes from widget.col.
 */
export function WidgetRenderer({ widget, data, isLoading, error }: WidgetRendererProps) {
  const { t } = useTranslation('common');
  const span = Math.min(Math.max(widget.col ?? DEFAULT_COL[widget.type] ?? 6, 1), 12);

  let body: ReactNode;
  switch (widget.type) {
    case 'kpi':
    case 'counter':
      body = <KpiCard widget={widget} data={data} isLoading={isLoading} error={error} />;
      break;
    case 'chart-line':
    case 'chart-bar':
    case 'chart-pie':
      body = <ChartCard widget={widget} data={data} isLoading={isLoading} error={error} />;
      break;
    case 'list':
      body = <ListWidget widget={widget} data={data} isLoading={isLoading} error={error} />;
      break;
    default:
      body = (
        <div className="rounded-[var(--radius-card)] border border-dashed border-[var(--border-color)] bg-[var(--bg-card)] p-4 text-sm text-[var(--text-muted)]">
          {t('dashboard.unknownWidget', { defaultValue: 'Unsupported widget' })}
        </div>
      );
  }

  return <div style={{ gridColumn: 'span ' + span + ' / span ' + span }}>{body}</div>;
}
`;
}

function localeFile(locale: typeof LOCALES[number]): GeneratedFile {
  return {
    path: `src/i18n/locales/${locale}/common.json`,
    content: JSON.stringify(I18N_STRINGS[locale], null, 2) + '\n',
    strategy: 'deep-merge-json',
  };
}

export function generate(_spec: ScaffoldDashboardPrimitivesInput): GeneratedFile[] {
  return [
    { path: 'src/components/dashboard/types.ts', content: typesModule(), strategy: 'overwrite' },
    { path: 'src/components/dashboard/useDatavizPalette.ts', content: useDatavizPaletteModule(), strategy: 'overwrite' },
    { path: 'src/components/dashboard/KpiCard.tsx', content: kpiCardComponent(), strategy: 'overwrite' },
    { path: 'src/components/dashboard/ChartCard.tsx', content: chartCardComponent(), strategy: 'overwrite' },
    { path: 'src/components/dashboard/ListWidget.tsx', content: listWidgetComponent(), strategy: 'overwrite' },
    { path: 'src/components/dashboard/DashboardGrid.tsx', content: dashboardGridComponent(), strategy: 'overwrite' },
    { path: 'src/components/dashboard/WidgetRenderer.tsx', content: widgetRendererComponent(), strategy: 'overwrite' },
    ...LOCALES.map(localeFile),
  ];
}
