"use client";

import { toastApiError } from "@/lib/toast-api-error";
import * as React from "react";
import { Loader2, Plus, Save, Trash2 } from "lucide-react";
import { toast } from "sonner";

import { Button } from "@/components/ui/button";
import { customerCsrfHeader } from "@/lib/customer-csrf.client";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet";
import { Textarea } from "@/components/ui/textarea";

import type {
  PortalCurrencyOption,
  PortalLinkBankAccountRow,
  PortalLinkBankCustomerRow,
  PortalParentBankOption,
  PortalStatusOption,
} from "@/app/customer/_lib/admin/link-bank-accounts-server-api";
import { ACTIVE_INACTIVE_STATUS_OPTIONS, normalizeActiveStatus } from "@/config/status-codes";

type EditableAccount = {
  key: string;
  id: number;
  bankName: string;
  accountNumber: string;
  moreDetails: string;
  status: "A" | "I";
  reportingCurrencyId: number | null;
  parentBankId: number | null;
};

type AccountsResponse = {
  status: string;
  message?: string;
  accounts?: PortalLinkBankAccountRow[];
  parentBankEnabled?: boolean;
  parentBankOptions?: PortalParentBankOption[];
  reportingCurrencyEnabled?: boolean;
  reportingCurrencyOptions?: PortalCurrencyOption[];
  statusOptions?: PortalStatusOption[];
};

const NONE_PARENT_VALUE = "__none__";
const NONE_CURRENCY_VALUE = "__none__";

const DEFAULT_STATUS_OPTIONS: PortalStatusOption[] = ACTIVE_INACTIVE_STATUS_OPTIONS;

let uidCounter = 0;
function nextKey() {
  uidCounter += 1;
  return `new-${uidCounter}`;
}

function toEditable(row: PortalLinkBankAccountRow): EditableAccount {
  return {
    key: `existing-${row.id}`,
    id: row.id,
    bankName: row.bankName,
    accountNumber: row.accountNumber,
    moreDetails: row.moreDetails,
    status: normalizeActiveStatus(row.status),
    reportingCurrencyId: row.reportingCurrencyId ?? null,
    parentBankId: row.parentBankId ?? null,
  };
}

function emptyRow(): EditableAccount {
  return {
    key: nextKey(),
    id: 0,
    bankName: "",
    accountNumber: "",
    moreDetails: "",
    status: "A",
    reportingCurrencyId: null,
    parentBankId: null,
  };
}

type BankAccountsSheetProps = {
  tenant: string;
  customer: PortalLinkBankCustomerRow | null;
  open: boolean;
  canManage: boolean;
  onOpenChange: (open: boolean) => void;
  onSaved?: (customerId: number, accountCount: number) => void;
};

export function BankAccountsSheet({
  tenant,
  customer,
  open,
  canManage,
  onOpenChange,
  onSaved,
}: BankAccountsSheetProps) {
  const [rows, setRows] = React.useState<EditableAccount[]>([]);
  const [parentBankEnabled, setParentBankEnabled] = React.useState(false);
  const [parentBankOptions, setParentBankOptions] = React.useState<PortalParentBankOption[]>([]);
  const [reportingCurrencyEnabled, setReportingCurrencyEnabled] = React.useState(false);
  const [reportingCurrencyOptions, setReportingCurrencyOptions] = React.useState<
    PortalCurrencyOption[]
  >([]);
  const [statusOptions, setStatusOptions] =
    React.useState<PortalStatusOption[]>(DEFAULT_STATUS_OPTIONS);
  const [isLoading, setIsLoading] = React.useState(false);
  const [isSaving, setIsSaving] = React.useState(false);
  const [loadError, setLoadError] = React.useState<string | null>(null);

  const customerId = customer?.customerId ?? 0;

  React.useEffect(() => {
    if (!open || customerId <= 0) return;

    let cancelled = false;

    const loadAccounts = async () => {
      setIsLoading(true);
      setLoadError(null);
      try {
        const response = await fetch(
          `/customer/${tenant}/admin/link-bank-accounts/accounts?customerId=${customerId}`,
          { method: "GET", headers: { Accept: "application/json" }, cache: "no-store" },
        );
        const data = (await response.json().catch(() => null)) as AccountsResponse | null;
        if (cancelled) return;
        if (!response.ok || !data || data.status !== "success") {
          throw new Error(
            typeof data?.message === "string" ? data.message : "Failed to load bank accounts.",
          );
        }
        const accounts = (data.accounts ?? []).map(toEditable);
        setRows(accounts.length ? accounts : [emptyRow()]);
        setParentBankEnabled(Boolean(data.parentBankEnabled));
        setParentBankOptions(data.parentBankOptions ?? []);
        setReportingCurrencyEnabled(Boolean(data.reportingCurrencyEnabled));
        setReportingCurrencyOptions(data.reportingCurrencyOptions ?? []);
        setStatusOptions(
          Array.isArray(data.statusOptions) && data.statusOptions.length
            ? data.statusOptions
            : DEFAULT_STATUS_OPTIONS,
        );
      } catch (error) {
        if (cancelled) return;
        setLoadError(error instanceof Error ? error.message : "Failed to load bank accounts.");
        setRows([emptyRow()]);
        setParentBankEnabled(false);
        setParentBankOptions([]);
        setReportingCurrencyEnabled(false);
        setReportingCurrencyOptions([]);
        setStatusOptions(DEFAULT_STATUS_OPTIONS);
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    };

    void loadAccounts();

    return () => {
      cancelled = true;
    };
  }, [open, customerId, tenant]);

  const updateRow = (key: string, patch: Partial<EditableAccount>) => {
    setRows((prev) => prev.map((row) => (row.key === key ? { ...row, ...patch } : row)));
  };

  const removeRow = (key: string) => {
    setRows((prev) => {
      const next = prev.filter((row) => row.key !== key);
      return next.length ? next : [emptyRow()];
    });
  };

  const addRow = () => setRows((prev) => [...prev, emptyRow()]);

  const handleSave = async () => {
    if (customerId <= 0 || !canManage) return;

    const filledRows = rows.filter(
      (row) => row.bankName.trim() !== "" || row.accountNumber.trim() !== "",
    );

    const invalidRow = filledRows.find(
      (row) => row.bankName.trim() === "" || row.accountNumber.trim() === "",
    );
    if (invalidRow) {
      toast.error("Each bank account needs both a bank name and an account number.");
      return;
    }

    if (reportingCurrencyEnabled) {
      const missingCurrency = filledRows.find(
        (row) => !row.reportingCurrencyId || row.reportingCurrencyId <= 0,
      );
      if (missingCurrency) {
        toast.error("Please select a reporting currency for each bank account.");
        return;
      }
    }

    setIsSaving(true);
    try {
      const response = await fetch(`/customer/${tenant}/admin/link-bank-accounts/save`, {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          ...customerCsrfHeader(tenant),
        },
        body: JSON.stringify({
          customerId,
          accounts: rows.map((row) => ({
            id: row.id > 0 ? row.id : undefined,
            bankName: row.bankName.trim(),
            accountNumber: row.accountNumber.trim(),
            moreDetails: row.moreDetails.trim(),
            status: row.status,
            reportingCurrencyId: reportingCurrencyEnabled ? row.reportingCurrencyId : undefined,
            parentBankId: parentBankEnabled ? row.parentBankId : undefined,
          })),
        }),
      });
      const data = (await response.json().catch(() => null)) as AccountsResponse | null;
      if (!response.ok || !data || data.status !== "success") {
        throw new Error(
          typeof data?.message === "string" ? data.message : "Failed to save bank accounts.",
        );
      }

      const saved = (data.accounts ?? []).map(toEditable);
      setRows(saved.length ? saved : [emptyRow()]);
      if (typeof data.parentBankEnabled === "boolean") {
        setParentBankEnabled(data.parentBankEnabled);
      }
      if (Array.isArray(data.parentBankOptions)) {
        setParentBankOptions(data.parentBankOptions);
      }
      if (typeof data.reportingCurrencyEnabled === "boolean") {
        setReportingCurrencyEnabled(data.reportingCurrencyEnabled);
      }
      if (Array.isArray(data.reportingCurrencyOptions)) {
        setReportingCurrencyOptions(data.reportingCurrencyOptions);
      }
      if (Array.isArray(data.statusOptions) && data.statusOptions.length) {
        setStatusOptions(data.statusOptions);
      }
      const activeCount = saved.filter((row) => row.status === "A").length;
      onSaved?.(customerId, activeCount);
      toast.success(data.message ?? "Bank accounts saved successfully.");
      onOpenChange(false);
    } catch (error) {
      toastApiError(error, "Failed to save bank accounts.");
    } finally {
      setIsSaving(false);
    }
  };

  const customerLabel =
    customer?.fullName?.trim() || (customerId > 0 ? `#${customerId}` : "");

  return (
    <Sheet open={open} onOpenChange={onOpenChange}>
      <SheetContent className="flex w-full flex-col gap-0 p-0 sm:max-w-2xl">
        <SheetHeader className="border-b px-6 py-4">
          <SheetTitle>Bank accounts</SheetTitle>
          <SheetDescription>
            {customerLabel
              ? `Manage bank accounts for ${customerLabel}.`
              : "Manage bank accounts."}
          </SheetDescription>
        </SheetHeader>

        <div className="flex-1 overflow-y-auto px-6 py-4">
          {isLoading ? (
            <div className="flex h-40 items-center justify-center text-muted-foreground">
              <Loader2 className="mr-2 size-4 animate-spin" />
              Loading bank accounts…
            </div>
          ) : (
            <div className="flex flex-col gap-4">
              {loadError ? (
                <p className="rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive">
                  {loadError}
                </p>
              ) : null}

              {rows.map((row, index) => (
                <div key={row.key} className="rounded-lg border bg-card p-4 shadow-sm">
                  <div className="mb-3 flex items-center justify-between">
                    <span className="text-sm font-medium text-muted-foreground">
                      Account {index + 1}
                    </span>
                    {canManage ? (
                      <Button
                        type="button"
                        variant="ghost"
                        size="icon"
                        className="size-8 text-muted-foreground hover:text-destructive"
                        onClick={() => removeRow(row.key)}
                        aria-label="Remove bank account"
                      >
                        <Trash2 className="size-4" />
                      </Button>
                    ) : null}
                  </div>
                  <div className="grid gap-3 sm:grid-cols-2">
                    <div className="flex flex-col gap-1.5">
                      <Label htmlFor={`bank-${row.key}`}>Bank name</Label>
                      <Input
                        id={`bank-${row.key}`}
                        value={row.bankName}
                        placeholder="e.g. HSBC"
                        disabled={!canManage}
                        onChange={(event) => updateRow(row.key, { bankName: event.target.value })}
                      />
                    </div>
                    <div className="flex flex-col gap-1.5">
                      <Label htmlFor={`account-${row.key}`}>Account number</Label>
                      <Input
                        id={`account-${row.key}`}
                        value={row.accountNumber}
                        placeholder="e.g. 1234567890"
                        disabled={!canManage}
                        onChange={(event) =>
                          updateRow(row.key, { accountNumber: event.target.value })
                        }
                      />
                    </div>
                  </div>
                  <div className="mt-3 grid gap-3 sm:grid-cols-2">
                    {reportingCurrencyEnabled ? (
                      <div className="flex flex-col gap-1.5">
                        <Label htmlFor={`currency-${row.key}`}>Reporting currency</Label>
                        <Select
                          value={
                            row.reportingCurrencyId && row.reportingCurrencyId > 0
                              ? String(row.reportingCurrencyId)
                              : NONE_CURRENCY_VALUE
                          }
                          disabled={!canManage}
                          onValueChange={(value) =>
                            updateRow(row.key, {
                              reportingCurrencyId:
                                value === NONE_CURRENCY_VALUE ? null : Number(value) || null,
                            })
                          }
                        >
                          <SelectTrigger id={`currency-${row.key}`} className="w-full">
                            <SelectValue placeholder="Please select" />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value={NONE_CURRENCY_VALUE}>Please select</SelectItem>
                            {reportingCurrencyOptions.map((option) => (
                              <SelectItem key={option.id} value={String(option.id)}>
                                {option.name}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      </div>
                    ) : null}
                    <div className="flex flex-col gap-1.5">
                      <Label htmlFor={`status-${row.key}`}>Status</Label>
                      <Select
                        value={row.status}
                        disabled={!canManage}
                        onValueChange={(value) =>
                          updateRow(row.key, { status: normalizeActiveStatus(value) })
                        }
                      >
                        <SelectTrigger id={`status-${row.key}`} className="w-full">
                          <SelectValue placeholder="Active" />
                        </SelectTrigger>
                        <SelectContent>
                          {statusOptions.map((option) => (
                            <SelectItem key={option.id} value={option.id}>
                              {option.name}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>
                  </div>
                  {parentBankEnabled ? (
                    <div className="mt-3 flex flex-col gap-1.5">
                      <Label htmlFor={`parent-${row.key}`}>Parent bank</Label>
                      <Select
                        value={
                          row.parentBankId && row.parentBankId > 0
                            ? String(row.parentBankId)
                            : NONE_PARENT_VALUE
                        }
                        disabled={!canManage}
                        onValueChange={(value) =>
                          updateRow(row.key, {
                            parentBankId:
                              value === NONE_PARENT_VALUE ? null : Number(value) || null,
                          })
                        }
                      >
                        <SelectTrigger id={`parent-${row.key}`} className="w-full">
                          <SelectValue placeholder="— None —" />
                        </SelectTrigger>
                        <SelectContent>
                          <SelectItem value={NONE_PARENT_VALUE}>— None —</SelectItem>
                          {parentBankOptions.map((option) => (
                            <SelectItem key={option.id} value={String(option.id)}>
                              {option.name}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>
                  ) : null}
                  <div className="mt-3 flex flex-col gap-1.5">
                    <Label htmlFor={`details-${row.key}`}>More details</Label>
                    <Textarea
                      id={`details-${row.key}`}
                      value={row.moreDetails}
                      placeholder="IBAN, SWIFT, branch, or any notes"
                      rows={2}
                      disabled={!canManage}
                      onChange={(event) => updateRow(row.key, { moreDetails: event.target.value })}
                    />
                  </div>
                </div>
              ))}

              {canManage ? (
                <Button
                  type="button"
                  variant="outline"
                  className="w-full border-dashed"
                  onClick={addRow}
                >
                  <Plus className="size-4" />
                  Add bank account
                </Button>
              ) : null}
            </div>
          )}
        </div>

        <SheetFooter className="flex-row justify-end gap-2 border-t px-6 py-4">
          <SheetClose asChild>
            <Button type="button" variant="outline" disabled={isSaving}>
              {canManage ? "Cancel" : "Close"}
            </Button>
          </SheetClose>
          {canManage ? (
            <Button
              type="button"
              onClick={() => void handleSave()}
              disabled={isSaving || isLoading}
            >
              {isSaving ? <Loader2 className="size-4 animate-spin" /> : <Save className="size-4" />}
              Save changes
            </Button>
          ) : null}
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}
