"use client";

import * as React from "react";
import {
  ArrowDown,
  ArrowUp,
  ChevronLeft,
  ChevronRight,
  Download,
  Plus,
  RefreshCw,
  Save,
  Search,
} from "lucide-react";
import { toast } from "sonner";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { formatPrice } from "@/lib/format/numbers";

import {
  createAssetDefinitionGroup,
  loadAssetDefinitionSettings,
  saveAssetDefinitionAssignments,
} from "../_lib/asset-definition-api";
import type {
  AssetDefinitionBridgeData,
  AssetDefinitionData,
  AssetDefinitionGroupSummaryData,
  AssetDefinitionView,
} from "../_lib/asset-definition-server-api";

type Filters = {
  macro: string;
  assetClass: string;
  assetType: string;
  groupName: string;
};

const EMPTY_FILTERS: Filters = { macro: "", assetClass: "", assetType: "", groupName: "" };
const NONE_VALUE = "__not_grouped__";

function isDefinitionData(
  data: AssetDefinitionData | AssetDefinitionBridgeData | AssetDefinitionGroupSummaryData | null,
): data is AssetDefinitionData {
  return data !== null && "pagination" in data && "groups" in data;
}

function number(value: number): string {
  return formatPrice(value, { minimumFractionDigits: 0, maximumFractionDigits: 2 });
}

/** Yii1 stores labels like `Fixed Income<small class="tik">Bond</small>`. */
function parseLegacyLabel(value: string | null | undefined): { primary: string; secondary: string } {
  const raw = String(value ?? "");
  const secondaryMatch = raw.match(/<small[^>]*class=["']?tik["']?[^>]*>([\s\S]*?)<\/small>/i);
  const secondary = secondaryMatch ? stripHtml(secondaryMatch[1]).trim() : "";
  const primary = stripHtml(raw.replace(/<small[^>]*class=["']?tik["']?[^>]*>[\s\S]*?<\/small>/gi, " ")).replace(/\s+/g, " ").trim();
  return { primary: primary || "—", secondary };
}

function stripHtml(value: string): string {
  return value
    .replace(/<[^>]*>/g, " ")
    .replace(/&nbsp;/gi, " ")
    .replace(/&amp;/gi, "&")
    .replace(/&lt;/gi, "<")
    .replace(/&gt;/gi, ">")
    .replace(/&quot;/gi, '"')
    .replace(/&#39;/g, "'")
    .replace(/\s+/g, " ")
    .trim();
}

function LegacyLabel({ value, className }: { value: string | null | undefined; className?: string }) {
  const { primary, secondary } = parseLegacyLabel(value);
  return (
    <div className={className}>
      <div className="whitespace-normal">{primary}</div>
      {secondary ? <div className="text-muted-foreground text-xs leading-tight">{secondary}</div> : null}
    </div>
  );
}

function plainLabel(value: string | null | undefined): string {
  const { primary, secondary } = parseLegacyLabel(value);
  return secondary ? `${primary} (${secondary})` : primary;
}

function csvCell(value: unknown): string {
  const text = plainLabel(String(value ?? ""));
  return `"${text.replaceAll('"', '""')}"`;
}

function downloadCsv(filename: string, headers: string[], rows: unknown[][]) {
  const csv = [headers, ...rows].map((row) => row.map(csvCell).join(",")).join("\n");
  const url = URL.createObjectURL(new Blob([csv], { type: "text/csv;charset=utf-8" }));
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  link.click();
  URL.revokeObjectURL(url);
}

export function SettingsPanelAssetDefinition({ tenant }: { tenant: string }) {
  const [view, setView] = React.useState<AssetDefinitionView>("definitions");
  const [data, setData] = React.useState<
    AssetDefinitionData | AssetDefinitionBridgeData | AssetDefinitionGroupSummaryData | null
  >(null);
  const [filters, setFilters] = React.useState<Filters>(EMPTY_FILTERS);
  const [appliedFilters, setAppliedFilters] = React.useState<Filters>(EMPTY_FILTERS);
  const [page, setPage] = React.useState(1);
  const [pageSize, setPageSize] = React.useState(50);
  const [sortField, setSortField] = React.useState("asset_class");
  const [sort, setSort] = React.useState<"asc" | "desc">("asc");
  const [changes, setChanges] = React.useState<Record<string, number | null>>({});
  const [newGroupName, setNewGroupName] = React.useState("");
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [creatingGroup, setCreatingGroup] = React.useState(false);
  const [error, setError] = React.useState<string | null>(null);

  const query = React.useMemo(() => {
    const params = new URLSearchParams();
    if (view === "definitions") {
      params.set("page", String(page));
      params.set("page_size", String(pageSize));
      params.set("sort_field", sortField);
      params.set("sort", sort);
      if (appliedFilters.macro) params.set("macro_asset_class", appliedFilters.macro);
      if (appliedFilters.assetClass) params.set("asetClass", appliedFilters.assetClass);
      if (appliedFilters.assetType) params.set("asset_type_title", appliedFilters.assetType);
      if (appliedFilters.groupName) params.set("group_name", appliedFilters.groupName);
    }
    return params;
  }, [appliedFilters, page, pageSize, sort, sortField, view]);

  const load = React.useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const result = await loadAssetDefinitionSettings(tenant, view, query);
      setData(result);
    } catch (loadError) {
      setError(loadError instanceof Error ? loadError.message : "Asset Definition settings could not be loaded.");
    } finally {
      setLoading(false);
    }
  }, [query, tenant, view]);

  React.useEffect(() => {
    const timer = window.setTimeout(() => void load(), 0);
    return () => window.clearTimeout(timer);
  }, [load]);

  function changeSort(field: string) {
    if (sortField === field) setSort((current) => current === "asc" ? "desc" : "asc");
    else {
      setSortField(field);
      setSort("asc");
    }
    setPage(1);
  }

  function sortIcon(field: string) {
    if (sortField !== field) return null;
    return sort === "asc" ? <ArrowUp className="size-3.5" /> : <ArrowDown className="size-3.5" />;
  }

  async function saveChanges() {
    if (Object.keys(changes).length === 0) {
      toast.info("No Asset Definition changes to save.");
      return;
    }
    setSaving(true);
    try {
      await saveAssetDefinitionAssignments(tenant, changes);
      setChanges({});
      toast.success("Asset Definition saved");
      await load();
    } catch (saveError) {
      toast.error(saveError instanceof Error ? saveError.message : "Asset Definition could not be saved.");
    } finally {
      setSaving(false);
    }
  }

  async function createGroup() {
    const name = newGroupName.trim();
    if (!name) {
      toast.error("Enter a group name.");
      return;
    }
    setCreatingGroup(true);
    try {
      const group = await createAssetDefinitionGroup(tenant, name);
      setData((current) => isDefinitionData(current)
        ? { ...current, groups: [...current.groups, group].sort((a, b) => a.name.localeCompare(b.name)) }
        : current);
      setNewGroupName("");
      toast.success("Group created");
    } catch (createError) {
      toast.error(createError instanceof Error ? createError.message : "Group could not be created.");
    } finally {
      setCreatingGroup(false);
    }
  }

  async function exportCurrentView() {
    try {
      const params = new URLSearchParams(query);
      params.set("all", "1");
      const exportData = await loadAssetDefinitionSettings(tenant, view, params);
      if (isDefinitionData(exportData)) {
        downloadCsv("asset-definition.csv",
          ["Macro Asset Class", "Asset Class", "Asset Type", "Group Name", "Value"],
          exportData.rows.map((row) => [
            plainLabel(row.macro_asset_class),
            plainLabel(row.asset_class),
            plainLabel(row.asset_type),
            row.group_name || "Not Grouped",
            row.value,
          ]));
      } else if (view === "bridge") {
        const bridge = exportData as AssetDefinitionBridgeData;
        downloadCsv("asset-definition-bridge.csv",
          ["Asset Class", "Asset Type", "Group Name", "Value"],
          bridge.rows.map((row) => [
            plainLabel(row.asset_class),
            plainLabel(row.asset_type),
            row.group_name || "Un Grouped",
            row.value,
          ]));
      } else {
        const summary = exportData as AssetDefinitionGroupSummaryData;
        downloadCsv("asset-definition-group-summary.csv",
          ["Group Name", "Value"],
          summary.rows.map((row) => [row.group_name, row.value]));
      }
    } catch (exportError) {
      toast.error(exportError instanceof Error ? exportError.message : "Export could not be generated.");
    }
  }

  return (
    <div className="space-y-5">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <Tabs value={view} onValueChange={(value) => setView(value as AssetDefinitionView)}>
          <TabsList>
            <TabsTrigger value="definitions">Asset Definition</TabsTrigger>
            <TabsTrigger value="bridge">Bridge Report</TabsTrigger>
            <TabsTrigger value="group-summary">Group Summary</TabsTrigger>
          </TabsList>
        </Tabs>
        <div className="flex gap-2">
          <Button type="button" variant="outline" size="sm" className="gap-2" onClick={() => void load()}>
            <RefreshCw className="size-4" /> Refresh
          </Button>
          <Button type="button" variant="outline" size="sm" className="gap-2" onClick={() => void exportCurrentView()}>
            <Download className="size-4" /> Export CSV
          </Button>
        </div>
      </div>

      {view === "definitions" && (
        <div className="grid gap-3 rounded-lg border bg-muted/20 p-3 lg:grid-cols-[1fr_1fr_1fr_1fr_auto]">
          <Input value={filters.macro} onChange={(event) => setFilters({ ...filters, macro: event.target.value })} placeholder="Macro asset class" />
          <Input value={filters.assetClass} onChange={(event) => setFilters({ ...filters, assetClass: event.target.value })} placeholder="Asset class" />
          <Input value={filters.assetType} onChange={(event) => setFilters({ ...filters, assetType: event.target.value })} placeholder="Asset type" />
          <Input value={filters.groupName} onChange={(event) => setFilters({ ...filters, groupName: event.target.value })} placeholder="Group name" />
          <Button type="button" className="gap-2" onClick={() => { setAppliedFilters(filters); setPage(1); }}>
            <Search className="size-4" /> Filter
          </Button>
        </div>
      )}

      {view === "definitions" && isDefinitionData(data) && (
        <div className="flex flex-wrap items-center gap-2 rounded-lg border p-3">
          <Input
            className="max-w-sm"
            value={newGroupName}
            onChange={(event) => setNewGroupName(event.target.value)}
            onKeyDown={(event) => {
              if (event.key === "Enter") {
                event.preventDefault();
                void createGroup();
              }
            }}
            placeholder="Enter a new group name"
          />
          <Button type="button" variant="secondary" className="gap-2" disabled={creatingGroup} onClick={() => void createGroup()}>
            <Plus className="size-4" /> {creatingGroup ? "Creating…" : "Create group"}
          </Button>
          <p className="ml-auto text-muted-foreground text-xs">
            {data.totals.grouped_count} grouped · {data.pagination.total_count} definitions
          </p>
        </div>
      )}

      {error && <p className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-destructive text-sm">{error}</p>}
      {loading && <p className="py-12 text-center text-muted-foreground text-sm">Loading Asset Definition…</p>}

      {!loading && !error && data && (
        <div className="overflow-hidden rounded-xl border bg-card">
          <div className="overflow-x-auto">
            {isDefinitionData(data) ? (
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead className="w-16">#</TableHead>
                    {[
                      ["macro_asset_class", "Macro Asset Class"],
                      ["asset_class", "Asset Class"],
                      ["asset_type", "Asset Type"],
                      ["group_name", "Group Name"],
                    ].map(([field, label]) => (
                      <TableHead key={field}>
                        <button type="button" className="flex items-center gap-1 font-medium" onClick={() => changeSort(field)}>
                          {label} {sortIcon(field)}
                        </button>
                      </TableHead>
                    ))}
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {data.rows.map((row, index) => {
                    const rowKey = row.ref_id || `definition-${index}-${row.asset_class}-${row.asset_type}`;
                    const selected = Object.hasOwn(changes, row.ref_id) ? changes[row.ref_id] : row.group_id;
                    const selectedValue =
                      selected === null || selected === undefined || Number(selected) <= 0
                        ? NONE_VALUE
                        : String(selected);
                    return (
                      <TableRow key={rowKey}>
                        <TableCell>{(data.pagination.page - 1) * data.pagination.page_size + index + 1}</TableCell>
                        <TableCell><LegacyLabel value={row.macro_asset_class} /></TableCell>
                        <TableCell><LegacyLabel value={row.asset_class} /></TableCell>
                        <TableCell className="max-w-md"><LegacyLabel value={row.asset_type} /></TableCell>
                        <TableCell className="min-w-64">
                          <Select
                            value={selectedValue}
                            onValueChange={(value) => {
                              if (!row.ref_id) return;
                              setChanges((current) => ({
                                ...current,
                                [row.ref_id]: value === NONE_VALUE ? null : Number(value),
                              }));
                            }}
                          >
                            <SelectTrigger><SelectValue placeholder="Not Grouped" /></SelectTrigger>
                            <SelectContent>
                              <SelectItem value={NONE_VALUE}>Not Grouped</SelectItem>
                              {data.groups
                                .filter((group) => Number.isFinite(group.id) && group.id > 0)
                                .map((group) => (
                                  <SelectItem key={group.id} value={String(group.id)}>
                                    {group.name || `Group ${group.id}`}
                                  </SelectItem>
                                ))}
                            </SelectContent>
                          </Select>
                        </TableCell>
                      </TableRow>
                    );
                  })}
                  {data.rows.length === 0 && (
                    <TableRow><TableCell colSpan={5} className="h-32 text-center text-muted-foreground">No asset definitions match the filters.</TableCell></TableRow>
                  )}
                </TableBody>
              </Table>
            ) : view === "bridge" ? (
              <Table>
                <TableHeader><TableRow><TableHead>#</TableHead><TableHead>Asset Class</TableHead><TableHead>Asset Type</TableHead><TableHead>Group Name</TableHead><TableHead className="text-right">Value</TableHead></TableRow></TableHeader>
                <TableBody>
                  {(data as AssetDefinitionBridgeData).rows.map((row, index) => (
                    <TableRow key={row.ref_id || `bridge-${index}-${row.asset_class}-${row.asset_type}`}>
                      <TableCell>{index + 1}</TableCell>
                      <TableCell><LegacyLabel value={row.asset_class} /></TableCell>
                      <TableCell className="max-w-md"><LegacyLabel value={row.asset_type} /></TableCell>
                      <TableCell>{row.group_name || "Un Grouped"}</TableCell>
                      <TableCell className="text-right tabular-nums">{number(row.value)}</TableCell>
                    </TableRow>
                  ))}
                </TableBody>
                <TableFooter><TableRow><TableCell colSpan={4}>Total</TableCell><TableCell className="text-right tabular-nums">{number(data.totals.value)}</TableCell></TableRow></TableFooter>
              </Table>
            ) : (
              <Table>
                <TableHeader><TableRow><TableHead>#</TableHead><TableHead>Group Name</TableHead><TableHead className="text-right">Value</TableHead></TableRow></TableHeader>
                <TableBody>
                  {(data as AssetDefinitionGroupSummaryData).rows.map((row, index) => (
                    <TableRow key={`${row.group_name || "ungrouped"}-${index}`}>
                      <TableCell>{index + 1}</TableCell>
                      <TableCell>{row.group_name || "Un Grouped"}</TableCell>
                      <TableCell className="text-right tabular-nums">{number(row.value)}</TableCell>
                    </TableRow>
                  ))}
                </TableBody>
                <TableFooter><TableRow><TableCell colSpan={2}>Total</TableCell><TableCell className="text-right tabular-nums">{number(data.totals.value)}</TableCell></TableRow></TableFooter>
              </Table>
            )}
          </div>
        </div>
      )}

      {view === "definitions" && isDefinitionData(data) && !loading && (
        <div className="flex flex-wrap items-center justify-between gap-3">
          <div className="flex items-center gap-2 text-muted-foreground text-sm">
            <span>Rows per page</span>
            <Select value={String(pageSize)} onValueChange={(value) => { setPageSize(Number(value)); setPage(1); }}>
              <SelectTrigger className="h-8 w-20"><SelectValue /></SelectTrigger>
              <SelectContent>{[25, 50, 100, 200].map((size) => <SelectItem key={size} value={String(size)}>{size}</SelectItem>)}</SelectContent>
            </Select>
            <span>{data.pagination.total_count} total</span>
          </div>
          <div className="flex items-center gap-2">
            <Button type="button" variant="outline" size="icon-sm" disabled={page <= 1} onClick={() => setPage((current) => current - 1)}><ChevronLeft /></Button>
            <span className="min-w-24 text-center text-sm">Page {data.pagination.page} of {data.pagination.page_count}</span>
            <Button type="button" variant="outline" size="icon-sm" disabled={page >= data.pagination.page_count} onClick={() => setPage((current) => current + 1)}><ChevronRight /></Button>
          </div>
        </div>
      )}

      {view === "definitions" && (
        <div className="flex items-center justify-end border-t pt-4">
          <Button type="button" className="gap-2" disabled={saving || Object.keys(changes).length === 0} onClick={() => void saveChanges()}>
            <Save className="size-4" /> {saving ? "Saving…" : `Save changes${Object.keys(changes).length ? ` (${Object.keys(changes).length})` : ""}`}
          </Button>
        </div>
      )}
    </div>
  );
}
