'use client';

import { useCallback, useMemo, useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { useMutation } from '@tanstack/react-query';
import { Download, FileText, Loader2, Play, Sparkles } from 'lucide-react';
import { toast } from 'sonner';

import { PageHeader } from '@/components/shared/page-header';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { DatePicker } from '@/components/ui/date-picker';
import { Label } from '@/components/ui/label';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table';
import { ReportTemplatesButton } from '@/components/reports/shared/report-templates-button';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { ENTITY_KEYS, ENTITY_REGISTRY, type EntityKey } from '@/lib/reports/custom/registry';
import { formatMoney, formatNumber } from '@/lib/reports/format-currency';

/**
 * Map from money-amount column → adjacent currency column. When both
 * are selected the on-screen + CSV output formats the amount with the
 * row's currency. When only the amount is selected we still pretty-
 * print with thousand separators but skip the currency glyph (the
 * analyst presumably has context elsewhere).
 */
const MONEY_COLUMN_PAIRS: Record<string, string> = {
  price: 'priceCurrency',
  depositExpectedAmount: 'depositExpectedCurrency',
};

function isMoneyColumnKey(key: string): boolean {
  return key in MONEY_COLUMN_PAIRS;
}

interface RunResponse {
  data: Array<Record<string, unknown>>;
  meta: {
    entity: EntityKey;
    columns: Array<{ key: string; label: string }>;
    rowCount: number;
  };
}

interface CustomTemplateConfig extends Record<string, unknown> {
  kind: 'custom';
  entity: EntityKey;
  columns: string[];
  from?: string;
  to?: string;
}

function defaultColumnsFor(entity: EntityKey): string[] {
  return ENTITY_REGISTRY[entity].columns.filter((c) => c.defaultSelected).map((c) => c.key);
}

export function CustomReportBuilder({ portSlug: _portSlug }: { portSlug: string }) {
  const searchParams = useSearchParams();
  const initialTemplateId = searchParams?.get('templateId') ?? null;

  const [entity, setEntity] = useState<EntityKey>('clients');
  const [columns, setColumns] = useState<string[]>(defaultColumnsFor('clients'));
  const [from, setFrom] = useState<string>('');
  const [to, setTo] = useState<string>('');
  const [activeTemplateId, setActiveTemplateId] = useState<string | null>(initialTemplateId);
  const [rows, setRows] = useState<Array<Record<string, unknown>>>([]);
  const [columnLabels, setColumnLabels] = useState<Array<{ key: string; label: string }>>([]);

  // When the user picks a different entity, reset columns to the
  // entity's defaults (carrying forward column keys would be confusing
  // since they're entity-specific). Also clear the active template
  // badge since the rep is composing a new query.
  function handleEntityChange(next: EntityKey) {
    setEntity(next);
    setColumns(defaultColumnsFor(next));
    setRows([]);
    setColumnLabels([]);
    setActiveTemplateId(null);
  }

  function toggleColumn(key: string, checked: boolean) {
    setColumns((prev) => {
      if (checked) return prev.includes(key) ? prev : [...prev, key];
      return prev.filter((k) => k !== key);
    });
    setActiveTemplateId(null);
  }

  const handleFromChange = useCallback((next: string) => {
    setFrom(next);
    setActiveTemplateId(null);
  }, []);

  const handleToChange = useCallback((next: string) => {
    setTo(next);
    setActiveTemplateId(null);
  }, []);

  const currentConfig: CustomTemplateConfig = useMemo(
    () => ({
      kind: 'custom',
      entity,
      columns,
      from: from || undefined,
      to: to || undefined,
    }),
    [entity, columns, from, to],
  );

  const handleApplyTemplate = useCallback((config: CustomTemplateConfig) => {
    // Raw setters: template apply MUST NOT clear the active-template
    // badge that the user-facing handlers above clear.
    if (config.entity) setEntity(config.entity);
    if (config.columns) setColumns(config.columns);
    setFrom(config.from ?? '');
    setTo(config.to ?? '');
    setRows([]);
    setColumnLabels([]);
  }, []);

  const runMutation = useMutation({
    mutationFn: async () => {
      // Convert the date-only YYYY-MM-DD strings (DatePicker output)
      // into ISO-8601 boundaries so the API zod schema accepts them.
      const fromIso = from ? new Date(`${from}T00:00:00.000Z`).toISOString() : undefined;
      const toIso = to ? new Date(`${to}T23:59:59.999Z`).toISOString() : undefined;
      return apiFetch<RunResponse>(`/api/v1/reports/custom/run`, {
        method: 'POST',
        body: {
          entity,
          columns,
          from: fromIso,
          to: toIso,
        },
      });
    },
    onSuccess: (res) => {
      setRows(res.data);
      setColumnLabels(res.meta.columns);
      toast.success(`Loaded ${res.meta.rowCount} rows`);
    },
    onError: (err) => toastError(err),
  });

  function downloadCsv() {
    if (rows.length === 0) {
      toast.error('Run the query first');
      return;
    }
    const headerLabels = columnLabels.map((c) => csvCell(c.label));
    const lines = [headerLabels.join(',')];
    for (const row of rows) {
      const cells = columnLabels.map((c) => csvCell(formatCellValue(c.key, row)));
      lines.push(cells.join(','));
    }
    const filenameSlug = `custom-${entity}`;
    const dateSuffix = new Date().toISOString().slice(0, 10);
    const filename = `${filenameSlug}-${dateSuffix}.csv`;
    const bom = '﻿';
    const blob = new Blob([bom + lines.join('\r\n') + '\r\n'], {
      type: 'text/csv;charset=utf-8',
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    setTimeout(() => URL.revokeObjectURL(url), 1000);
    toast.success(`Downloaded ${filename}`);
  }

  const def = ENTITY_REGISTRY[entity];

  return (
    <div className="space-y-6">
      <PageHeader
        eyebrow="Reports"
        title="Custom report"
        description="Pick an entity, choose columns, set an optional date range, download as CSV. Save the configuration as a template to re-run later."
        actions={
          <div className="flex items-center gap-2">
            <ReportTemplatesButton<CustomTemplateConfig>
              kind="custom"
              currentConfig={currentConfig}
              onApply={handleApplyTemplate}
              activeTemplateId={activeTemplateId}
              onActiveTemplateChange={setActiveTemplateId}
              initialTemplateId={initialTemplateId}
            />
          </div>
        }
      />

      <Card>
        <CardContent className="space-y-4 pt-6">
          <div className="grid grid-cols-1 gap-4 lg:grid-cols-[260px_1fr] lg:items-start">
            <div className="space-y-3">
              <div className="space-y-1.5">
                <Label htmlFor="custom-entity" className="text-xs">
                  Entity
                </Label>
                <Select value={entity} onValueChange={(v) => handleEntityChange(v as EntityKey)}>
                  <SelectTrigger id="custom-entity">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {ENTITY_KEYS.map((k) => (
                      <SelectItem key={k} value={k}>
                        {ENTITY_REGISTRY[k].label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <p className="text-[11px] text-muted-foreground">{def.description}</p>
              </div>

              <div className="space-y-1.5">
                <Label className="text-xs">Date filter ({def.dateAxis})</Label>
                <div className="space-y-1.5">
                  <DatePicker
                    id="custom-date-from"
                    value={from}
                    onChange={handleFromChange}
                    placeholder="From"
                    size="sm"
                  />
                  <DatePicker
                    id="custom-date-to"
                    value={to}
                    onChange={handleToChange}
                    placeholder="To"
                    size="sm"
                  />
                </div>
                <p className="text-[11px] text-muted-foreground">
                  Optional. Leave blank for all-time.
                </p>
              </div>

              <div className="flex flex-col gap-2 pt-2">
                <Button
                  onClick={() => runMutation.mutate()}
                  disabled={runMutation.isPending || columns.length === 0}
                  size="sm"
                >
                  {runMutation.isPending ? (
                    <Loader2 className="mr-1.5 h-4 w-4 animate-spin" aria-hidden />
                  ) : (
                    <Play className="mr-1.5 h-4 w-4" aria-hidden />
                  )}
                  Run query
                </Button>
                <Button
                  onClick={downloadCsv}
                  variant="outline"
                  size="sm"
                  disabled={rows.length === 0}
                >
                  <Download className="mr-1.5 h-4 w-4" aria-hidden />
                  Download CSV
                </Button>
              </div>
            </div>

            <div className="space-y-2">
              <Label className="text-xs">Columns</Label>
              <div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
                {def.columns.map((c) => {
                  const checked = columns.includes(c.key);
                  return (
                    <label
                      key={c.key}
                      className="flex cursor-pointer items-center gap-2 rounded-md border bg-muted/20 px-2 py-1.5 text-sm hover:bg-muted/40"
                    >
                      <Checkbox
                        checked={checked}
                        onCheckedChange={(v) => toggleColumn(c.key, Boolean(v))}
                      />
                      <span>{c.label}</span>
                    </label>
                  );
                })}
              </div>
              {columns.length === 0 ? (
                <p className="text-xs text-amber-600">Select at least one column to run.</p>
              ) : (
                <p className="text-[11px] text-muted-foreground">
                  {columns.length} of {def.columns.length} columns selected.
                </p>
              )}
            </div>
          </div>
        </CardContent>
      </Card>

      {/* Results table — only shows after Run query. Caps the visible
          rows; CSV export gives the full set. */}
      {rows.length > 0 ? (
        <Card>
          <CardContent className="p-0">
            <div className="flex items-center justify-between border-b bg-muted/30 px-4 py-2 text-xs">
              <span className="font-medium">{rows.length} rows</span>
              <span className="text-muted-foreground">
                Showing first {Math.min(rows.length, 50)} · download CSV for full set
              </span>
            </div>
            <div className="overflow-x-auto">
              <Table>
                <TableHeader>
                  <TableRow>
                    {columnLabels.map((c) => (
                      <TableHead key={c.key}>{c.label}</TableHead>
                    ))}
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {rows.slice(0, 50).map((row, idx) => (
                    <TableRow key={idx}>
                      {columnLabels.map((c) => (
                        <TableCell key={c.key} className="text-sm">
                          {formatCellValue(c.key, row)}
                        </TableCell>
                      ))}
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </div>
          </CardContent>
        </Card>
      ) : runMutation.isSuccess ? (
        <Card>
          <CardContent className="flex flex-col items-center justify-center py-12 text-center">
            <FileText className="mb-3 h-8 w-8 text-muted-foreground" aria-hidden />
            <p className="text-sm font-medium">No rows match this query</p>
            <p className="mt-1 text-xs text-muted-foreground">
              Try widening the date range, picking a different entity, or removing filters.
            </p>
          </CardContent>
        </Card>
      ) : (
        <Card>
          <CardContent className="flex flex-col items-center justify-center py-12 text-center">
            <Sparkles className="mb-3 h-8 w-8 text-muted-foreground" aria-hidden />
            <p className="text-sm font-medium">Configure your query above, then Run.</p>
            <p className="mt-1 text-xs text-muted-foreground">
              Results appear here. Save the configuration as a template to schedule recurring runs
              or share it with the team.
            </p>
          </CardContent>
        </Card>
      )}
    </div>
  );
}

/**
 * Per-cell value formatter. Falls through to a generic string render
 * except for known money columns (per MONEY_COLUMN_PAIRS) where we
 * pretty-print with the row's currency when available. Numeric columns
 * outside the money set get thousand-separator formatting for
 * readability.
 */
function formatCellValue(key: string, row: Record<string, unknown>): string {
  const v = row[key];
  if (v === null || v === undefined) return '';

  if (isMoneyColumnKey(key) && typeof v === 'number') {
    const currencyKey = MONEY_COLUMN_PAIRS[key];
    if (currencyKey) {
      const ccy = row[currencyKey];
      if (typeof ccy === 'string' && ccy.length > 0) return formatMoney(v, ccy);
    }
    // Currency unknown — drop the glyph, keep the readable number.
    return formatNumber(v);
  }

  if (v instanceof Date) return v.toISOString().slice(0, 10);
  if (typeof v === 'number') return formatNumber(v);
  if (typeof v === 'string') {
    if (/^\d{4}-\d{2}-\d{2}T/.test(v)) return v.slice(0, 10);
    return v;
  }
  return String(v);
}

function csvCell(value: string): string {
  if (value === '') return '""';
  return `"${value.replace(/"/g, '""')}"`;
}
