'use client';

import { useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Eye, FileDown, Loader2 } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { DatePicker } from '@/components/ui/date-picker';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { usePermissions } from '@/hooks/use-permissions';
import { usePortContext } from '@/providers/port-provider';
import {
  PDF_DASHBOARD_WIDGETS,
  PDF_DASHBOARD_CATEGORY_LABELS,
  type PdfDashboardWidgetId,
  type PdfDashboardWidgetCategory,
} from '@/lib/services/dashboard-report-widgets';
import { triggerBlobDownload } from '@/lib/utils/download';
import { resolvePortIdFromSlug } from '@/lib/api/client';
import {
  SavedTemplatesPicker,
  type SavedTemplate,
} from '@/components/reports/saved-templates-picker';
import { PdfPreviewModal } from '@/components/reports/pdf-preview-modal';

function toIsoLocal(d: Date): string {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}

interface Props {
  portSlug: string;
  /** YYYY-MM-DD from a URL search-param so deep-links from the dashboard
   *  Export button can pre-fill the range the rep was already viewing. */
  initialFrom?: string;
  initialTo?: string;
}

/**
 * Page-mounted Dashboard report builder. Migrates the export dialog body
 * into a dedicated page (Reports P4). Same widget grouping + date-range
 * controls + preview + saved-templates picker; the only behaviour change
 * is that submit no longer closes a Dialog — it stays on the builder so
 * the rep can tweak + re-export.
 */
export function DashboardReportBuilder({ portSlug, initialFrom, initialTo }: Props) {
  const router = useRouter();
  const [title, setTitle] = useState(`Report - ${new Date().toLocaleDateString(undefined)}`);
  const [subtitle, setSubtitle] = useState('');
  const [selected, setSelected] = useState<PdfDashboardWidgetId[]>(
    PDF_DASHBOARD_WIDGETS.map((w) => w.id),
  );
  const today = new Date();
  const last30 = new Date(today);
  last30.setDate(last30.getDate() - 30);
  const [dateFrom, setDateFrom] = useState(initialFrom ?? toIsoLocal(last30));
  const [dateTo, setDateTo] = useState(initialTo ?? toIsoLocal(today));
  const [outputFormat, setOutputFormat] = useState<'pdf' | 'csv'>('pdf');
  const [coverBrandPortId, setCoverBrandPortId] = useState<string>('');
  const [loading, setLoading] = useState(false);
  const [enqueuing, setEnqueuing] = useState(false);
  const [previewOpen, setPreviewOpen] = useState(false);

  // P7: cover-brand swap — admin-only. The renderer falls back to the
  // active port's brand kit when this is empty or invalid.
  const { can } = usePermissions();
  const { ports, currentPortId } = usePortContext();
  const canPickBrand = can('admin', 'manage_settings') && ports.length > 1;

  const previewPayload = useMemo(
    () => ({
      title: title.trim() || 'Report',
      config: {
        kind: 'dashboard' as const,
        widgetIds: selected,
        ...(subtitle.trim() ? { subtitle: subtitle.trim() } : {}),
        ...(coverBrandPortId ? { coverBrandPortId } : {}),
        ...(dateFrom ? { dateFrom } : {}),
        ...(dateTo ? { dateTo } : {}),
      },
    }),
    [title, subtitle, selected, coverBrandPortId, dateFrom, dateTo],
  );

  function toggle(id: PdfDashboardWidgetId) {
    setSelected((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
  }

  async function portHeader(): Promise<HeadersInit> {
    const headers = new Headers({ 'Content-Type': 'application/json' });
    const portId = await resolvePortIdFromSlug(portSlug);
    if (portId) headers.set('X-Port-Id', portId);
    return headers;
  }

  async function handleDownload() {
    if (selected.length === 0) {
      toast.error('Pick at least one section to include.');
      return;
    }
    setLoading(true);
    try {
      const res = await fetch('/api/v1/reports/generate', {
        method: 'POST',
        headers: await portHeader(),
        body: JSON.stringify({
          title: title.trim() || 'Report',
          config: {
            kind: 'dashboard',
            widgetIds: selected,
            ...(subtitle.trim() ? { subtitle: subtitle.trim() } : {}),
            ...(coverBrandPortId ? { coverBrandPortId } : {}),
            ...(dateFrom ? { dateFrom } : {}),
            ...(dateTo ? { dateTo } : {}),
          },
        }),
      });
      if (!res.ok) {
        const text = await res.text();
        throw new Error(text || `Export failed (${res.status})`);
      }
      const blob = await res.blob();
      const filename = title.trim().replace(/[\\/]/g, '_') + '.pdf';
      triggerBlobDownload(blob, filename);
      toast.success('Report downloaded');
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Export failed');
    } finally {
      setLoading(false);
    }
  }

  /**
   * P3 path: enqueue a `report_runs` row instead of doing a synchronous
   * download. The BullMQ worker picks it up, renders, and parks the file
   * on the report row. The rep then sees it in the /reports/runs list.
   */
  async function handleEnqueueRun() {
    if (selected.length === 0) {
      toast.error('Pick at least one section to include.');
      return;
    }
    setEnqueuing(true);
    try {
      const res = await fetch('/api/v1/reports/runs', {
        method: 'POST',
        headers: await portHeader(),
        body: JSON.stringify({
          kind: 'dashboard',
          config: {
            kind: 'dashboard',
            widgetIds: selected,
            title: title.trim() || 'Report',
            ...(subtitle.trim() ? { subtitle: subtitle.trim() } : {}),
            ...(coverBrandPortId ? { coverBrandPortId } : {}),
            ...(dateFrom ? { dateFrom } : {}),
            ...(dateTo ? { dateTo } : {}),
          },
          outputFormat,
        }),
      });
      if (!res.ok) {
        const text = await res.text();
        throw new Error(text || `Enqueue failed (${res.status})`);
      }
      toast.success('Report queued — track progress in Runs.');
      router.push(`/${portSlug}/reports/runs`);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Enqueue failed');
    } finally {
      setEnqueuing(false);
    }
  }

  return (
    <div className="space-y-4">
      <SavedTemplatesPicker
        kind="dashboard"
        currentConfig={{ widgetIds: selected }}
        onApply={(t: SavedTemplate) => {
          const cfg = t.config as { widgetIds?: string[] };
          if (Array.isArray(cfg.widgetIds)) {
            setSelected(
              cfg.widgetIds.filter((id): id is PdfDashboardWidgetId =>
                PDF_DASHBOARD_WIDGETS.some((w) => w.id === id),
              ),
            );
          }
          if (t.name) setTitle(t.name);
        }}
      />

      <Card>
        <CardHeader>
          <CardTitle className="text-sm">Title + window</CardTitle>
        </CardHeader>
        <CardContent className="space-y-4">
          <div className="space-y-1">
            <Label htmlFor="export-title">Title</Label>
            <Input
              id="export-title"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              className="max-w-md"
            />
          </div>
          <div className="space-y-1">
            <Label htmlFor="export-subtitle">
              Subtitle <span className="text-xs font-normal text-muted-foreground">(optional)</span>
            </Label>
            <Input
              id="export-subtitle"
              value={subtitle}
              onChange={(e) => setSubtitle(e.target.value)}
              className="max-w-md"
              placeholder="e.g. Board pack — March 2026"
            />
          </div>
          <div className="space-y-1">
            <Label>Report window</Label>
            <div className="flex flex-wrap items-center gap-2">
              <DatePicker
                id="export-date-from"
                value={dateFrom}
                onChange={setDateFrom}
                placeholder="Start"
                size="sm"
                className="w-[150px]"
              />
              <span className="text-xs text-muted-foreground">→</span>
              <DatePicker
                id="export-date-to"
                value={dateTo}
                onChange={setDateTo}
                placeholder="End"
                size="sm"
                className="w-[150px]"
              />
              <Button
                type="button"
                variant="ghost"
                size="sm"
                onClick={() => {
                  const t = new Date();
                  const start = new Date(t);
                  start.setDate(start.getDate() - 30);
                  setDateFrom(toIsoLocal(start));
                  setDateTo(toIsoLocal(t));
                }}
                className="h-8 text-xs"
              >
                Last 30 days
              </Button>
              <Button
                type="button"
                variant="ghost"
                size="sm"
                onClick={() => {
                  const t = new Date();
                  const start = new Date(t);
                  start.setDate(start.getDate() - 90);
                  setDateFrom(toIsoLocal(start));
                  setDateTo(toIsoLocal(t));
                }}
                className="h-8 text-xs"
              >
                Last 90 days
              </Button>
            </div>
            <p className="text-xs text-muted-foreground">
              Drives time-period sections (new clients, berths sold, occupancy timeline, etc.).
              Sections marked &ldquo;needs date range&rdquo; only render when both dates are set.
            </p>
          </div>
          {canPickBrand ? (
            <div className="space-y-1">
              <Label htmlFor="export-brand">
                Cover-page brand{' '}
                <span className="text-xs font-normal text-muted-foreground">
                  (admin-only, defaults to the active port)
                </span>
              </Label>
              <Select
                value={coverBrandPortId || 'default'}
                onValueChange={(v) => setCoverBrandPortId(v === 'default' ? '' : v)}
              >
                <SelectTrigger id="export-brand" className="max-w-md">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="default">Use active port brand</SelectItem>
                  {ports
                    .filter((p) => p.id !== currentPortId)
                    .map((p) => (
                      <SelectItem key={p.id} value={p.id}>
                        {p.name}
                      </SelectItem>
                    ))}
                </SelectContent>
              </Select>
              <p className="text-xs text-muted-foreground">
                Swaps the cover-page logo and port name to the picked brand. The data inside stays
                from the active port.
              </p>
            </div>
          ) : null}
          <div className="space-y-1">
            <Label>Output format</Label>
            <div className="flex gap-2">
              {(['pdf', 'csv'] as const).map((f) => (
                <Button
                  key={f}
                  type="button"
                  size="sm"
                  variant={outputFormat === f ? 'default' : 'outline'}
                  onClick={() => setOutputFormat(f)}
                >
                  {f.toUpperCase()}
                </Button>
              ))}
            </div>
            <p className="text-xs text-muted-foreground">
              PDF carries the brand kit + section layout; CSV emits a flat metric-per-row dump
              suitable for spreadsheet analysis. CSV applies to the queued-run path only.
            </p>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="text-sm">Sections</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="max-h-[60vh] space-y-3 overflow-y-auto rounded-md border p-2">
            {(
              Object.entries(PDF_DASHBOARD_CATEGORY_LABELS) as Array<
                [PdfDashboardWidgetCategory, string]
              >
            ).map(([category, label]) => {
              const items = PDF_DASHBOARD_WIDGETS.filter((w) => w.category === category);
              if (items.length === 0) return null;
              return (
                <div key={category} className="space-y-1">
                  <div className="px-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                    {label}
                  </div>
                  <div className="space-y-0.5">
                    {items.map((w) => (
                      <label
                        key={w.id}
                        className="flex items-start gap-2 cursor-pointer rounded-sm p-1 hover:bg-muted/50"
                      >
                        <Checkbox
                          checked={selected.includes(w.id)}
                          onCheckedChange={() => toggle(w.id)}
                          aria-label={w.label}
                        />
                        <div className="text-sm leading-tight">
                          <div className="flex items-center gap-1.5">
                            <span className="font-medium">{w.label}</span>
                            {w.isChart ? (
                              <span className="shrink-0 whitespace-nowrap rounded-full bg-primary/10 px-1.5 py-px text-[8px] font-medium uppercase tracking-wide leading-none text-primary">
                                chart
                              </span>
                            ) : null}
                            {w.requiresPeriod ? (
                              <span className="shrink-0 whitespace-nowrap rounded-full bg-amber-100 px-1.5 py-px text-[8px] font-medium uppercase tracking-wide leading-none text-amber-800">
                                needs date range
                              </span>
                            ) : null}
                          </div>
                          <div className="text-xs text-muted-foreground">{w.description}</div>
                        </div>
                      </label>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>
        </CardContent>
      </Card>

      <div className="flex flex-wrap items-center justify-end gap-2">
        <Button
          variant="outline"
          onClick={() => setPreviewOpen(true)}
          disabled={loading || enqueuing || selected.length === 0}
        >
          <Eye className="mr-1.5 h-4 w-4" aria-hidden />
          Preview
        </Button>
        <Button
          variant="outline"
          onClick={handleEnqueueRun}
          disabled={loading || enqueuing || selected.length === 0}
        >
          {enqueuing ? <Loader2 className="mr-1.5 h-4 w-4 animate-spin" aria-hidden /> : null}
          Queue + go to Runs
        </Button>
        <Button onClick={handleDownload} disabled={loading || enqueuing || selected.length === 0}>
          {loading ? (
            <Loader2 className="mr-1.5 h-4 w-4 animate-spin" aria-hidden />
          ) : (
            <FileDown className="mr-1.5 h-4 w-4" aria-hidden />
          )}
          Download PDF
        </Button>
      </div>

      {previewOpen ? (
        <PdfPreviewModal
          open
          onOpenChange={setPreviewOpen}
          payload={previewPayload}
          filename={`${title.trim().replace(/[\\/]/g, '_') || 'report'}.pdf`}
          title={`Preview: ${title.trim() || 'Report'}`}
        />
      ) : null}
    </div>
  );
}
