"use client";

/**
 * Typed operational document workflows for domains without dedicated engines yet.
 * Still real /ops API — NOT the generic number/party/amount stub.
 */
import { useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Plus } from "lucide-react";
import { toast } from "sonner";
import { z } from "zod";
import { accountingApi } from "@/src/modules/accounting/services/accounting-api";
import { useTenantId } from "@/hooks/useTenantId";
import { formatMoney, parseMoneyInput } from "@/lib/utils";
import {
  Badge,
  Button,
  DataTable,
  DatePicker,
  Dialog,
  EmptyState,
  ErrorState,
  FormField,
  Input,
  JalaliDateText,
  LoadingState,
  MoneyInput,
  PageHeader,
  Select,
} from "@/src/shared/ui";
import { PartyCombobox } from "@/src/modules/accounting/components/EntityCombobox";

type FieldDef =
  | {
      key: string;
      label: string;
      kind: "text" | "money" | "date" | "select" | "cash_box" | "bank_account";
      options?: { value: string; label: string }[];
      required?: boolean;
      /** Show field only when another field equals this value */
      showWhen?: { key: string; value: string | string[] };
    }
  | { key: string; label: string; kind: "party"; module?: string; required?: boolean; showWhen?: { key: string; value: string | string[] } };

function isShown(f: FieldDef, values: Record<string, string>): boolean {
  if (!f.showWhen) return true;
  const current = values[f.showWhen.key] ?? "";
  const expected = f.showWhen.value;
  return Array.isArray(expected) ? expected.includes(current) : current === expected;
}

export function SpecializedOpsPage({
  title,
  description,
  module,
  docType,
  createLabel,
  fields,
  amountKey = "amount",
}: {
  title: string;
  description: string;
  module: string;
  docType: string;
  createLabel: string;
  fields: FieldDef[];
  amountKey?: string;
}) {
  const { tenantId } = useTenantId();
  const qc = useQueryClient();
  const [open, setOpen] = useState(false);

  const needsCash = fields.some((f) => f.kind === "cash_box");
  const needsBank = fields.some((f) => f.kind === "bank_account");

  const cashBoxesQ = useQuery({
    queryKey: ["accounting", tenantId, "cash-boxes"],
    queryFn: () => accountingApi.treasury.listCashBoxes(tenantId!),
    enabled: !!tenantId && needsCash,
  });
  const bankAccountsQ = useQuery({
    queryKey: ["accounting", tenantId, "bank-accounts"],
    queryFn: () => accountingApi.treasury.listBankAccounts(tenantId!),
    enabled: !!tenantId && needsBank,
  });
  const banksQ = useQuery({
    queryKey: ["accounting", tenantId, "banks"],
    queryFn: () => accountingApi.treasury.listBanks(tenantId!),
    enabled: !!tenantId && needsBank,
  });
  const bankNameById = useMemo(() => {
    const m = new Map<string, string>();
    for (const b of banksQ.data ?? []) m.set(b.id, b.name);
    return m;
  }, [banksQ.data]);

  const shape: Record<string, z.ZodTypeAny> = {
    number: z.string().optional(),
    doc_date: z.string().min(1, "تاریخ الزامی است"),
    party_name: z.string().optional(),
    party_id: z.string().optional(),
    description: z.string().optional(),
  };
  for (const f of fields) {
    if (f.kind === "party") continue;
    // Conditional fields validated in submit — keep optional in zod
    if (f.showWhen) {
      shape[f.key] = z.string().optional();
    } else {
      shape[f.key] = f.required === false ? z.string().optional() : z.string().min(1, `${f.label} الزامی است`);
    }
  }
  const schema = z.object(shape);
  type Values = z.infer<typeof schema>;

  const defaults = useMemo(() => {
    const d: Record<string, string> = {
      number: "",
      doc_date: new Date().toISOString().slice(0, 10),
      party_name: "",
      party_id: "",
      description: "",
    };
    for (const f of fields) {
      if (f.kind === "party") continue;
      d[f.key] = f.kind === "money" ? "0" : f.kind === "date" ? new Date().toISOString().slice(0, 10) : "";
    }
    return d as Values;
  }, [fields]);

  const form = useForm<Values>({ resolver: zodResolver(schema), defaultValues: defaults });
  const watched = form.watch() as Record<string, string>;

  const listQ = useQuery({
    queryKey: ["accounting", tenantId, "ops-docs", module, docType],
    queryFn: () => accountingApi.ops.listDocuments(tenantId!, module, docType),
    enabled: !!tenantId,
  });

  const createM = useMutation({
    mutationFn: (v: Values) => {
      const values = v as Record<string, string>;
      for (const f of fields) {
        if (f.kind === "party") continue;
        if (!isShown(f, values)) continue;
        if (f.required === false) continue;
        const raw = String(values[f.key] ?? "").trim();
        if (!raw || (f.kind === "money" && Number(parseMoneyInput(raw) || "0") <= 0 && f.key === amountKey)) {
          if (f.kind === "money" && f.key === amountKey && Number(parseMoneyInput(raw) || "0") <= 0) {
            throw new Error(`${f.label} باید بزرگ‌تر از صفر باشد`);
          }
          if (f.kind !== "money" && !raw) {
            throw new Error(`${f.label} الزامی است`);
          }
        }
      }

      const meta: Record<string, string> = {};
      for (const f of fields) {
        if (f.kind === "party") continue;
        if (!isShown(f, values)) continue;
        const raw = String(values[f.key] ?? "");
        meta[f.key] = f.kind === "money" ? parseMoneyInput(raw) || "0" : raw;
        if (f.kind === "cash_box") {
          const box = (cashBoxesQ.data ?? []).find((c) => c.id === raw);
          if (box) meta.cash_box_name = box.name;
        }
        if (f.kind === "bank_account") {
          const bank = (bankAccountsQ.data ?? []).find((b) => b.id === raw);
          if (bank) {
            meta.bank_account_number = bank.account_number;
            meta.bank_name = bankNameById.get(bank.bank_id) || "";
          }
        }
      }
      const amount = parseMoneyInput(String(values[amountKey] ?? meta[amountKey] ?? "0")) || "0";
      return accountingApi.ops.createDocument(tenantId!, {
        module,
        doc_type: docType,
        number: String(v.number || "").trim() || undefined,
        doc_date: String(v.doc_date),
        party_name: v.party_name ? String(v.party_name) : undefined,
        party_id: v.party_id ? String(v.party_id) : undefined,
        amount,
        description: v.description ? String(v.description) : undefined,
        status: "draft",
        meta_json: JSON.stringify(meta),
      });
    },
    onSuccess: async () => {
      toast.success(`${title} ثبت شد`);
      setOpen(false);
      form.reset(defaults);
      await qc.invalidateQueries({ queryKey: ["accounting", tenantId, "ops-docs", module, docType] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const openCreate = async () => {
    form.reset(defaults);
    setOpen(true);
    try {
      const seq = await accountingApi.setup.peekNumber(tenantId!, `ops.${module}.${docType}`);
      form.setValue("number" as never, seq.preview as never);
    } catch {
      /* BE allocates on blank */
    }
  };
  const confirmM = useMutation({
    mutationFn: (id: string) => accountingApi.ops.confirmDocument(tenantId!, id),
    onSuccess: async () => {
      toast.success("تأیید شد");
      await qc.invalidateQueries({ queryKey: ["accounting", tenantId, "ops-docs", module, docType] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  if (!tenantId || listQ.isLoading) return <LoadingState />;
  if (listQ.error) return <ErrorState message={listQ.error.message} onRetry={() => listQ.refetch()} />;

  const methodLabel = (r: Record<string, unknown>) => {
    try {
      const meta = JSON.parse(String(r.meta_json || "{}")) as Record<string, string>;
      if (meta.method === "cash") return meta.cash_box_name ? `نقد — ${meta.cash_box_name}` : "نقد";
      if (meta.method === "bank")
        return meta.bank_account_number
          ? `بانک — ${meta.bank_name ? meta.bank_name + " " : ""}${meta.bank_account_number}`
          : "بانک";
      if (meta.method === "cheque") return "چک";
      return meta.method || "—";
    } catch {
      return "—";
    }
  };

  return (
    <div>
      <PageHeader
        title={title}
        description={description}
        actions={
          <Button type="button" onClick={() => void openCreate()}>
            <Plus className="h-4 w-4" />
            {createLabel}
          </Button>
        }
      />
      <DataTable
        columns={[
          { key: "number", header: "شماره" },
          { key: "doc_date", header: "تاریخ", render: (r) => <JalaliDateText value={String(r.doc_date ?? "")} /> },
          { key: "party_name", header: "طرف حساب" },
          { key: "amount", header: "مبلغ", render: (r) => formatMoney(String(r.amount ?? "0")) },
          {
            key: "method",
            header: "روش / حساب",
            render: (r) => methodLabel(r),
          },
          {
            key: "status",
            header: "وضعیت",
            render: (r) => <Badge tone={r.status === "confirmed" ? "success" : "default"}>{String(r.status)}</Badge>,
          },
          {
            key: "id",
            header: "عملیات",
            render: (r) =>
              r.status === "draft" ? (
                <Button size="sm" variant="outline" onClick={() => confirmM.mutate(String(r.id))}>
                  تأیید
                </Button>
              ) : null,
          },
        ]}
        rows={(listQ.data ?? []) as unknown as Record<string, unknown>[]}
        empty={
          <EmptyState
            title={`${title} ثبت نشده`}
            action={
              <Button type="button" onClick={() => void openCreate()}>
                <Plus className="h-4 w-4" />
                {createLabel}
              </Button>
            }
          />
        }
      />
      <Dialog open={open} onClose={() => setOpen(false)} title={createLabel} size="lg">
        <form className="grid gap-3 sm:grid-cols-2" onSubmit={form.handleSubmit((v) => createM.mutate(v))}>
          <FormField label="شماره" error={form.formState.errors.number?.message as string | undefined}>
            <Input {...form.register("number")} placeholder="خودکار از سیستم" />
          </FormField>
          <FormField label="تاریخ">
            <Controller
              control={form.control}
              name="doc_date"
              render={({ field }) => (
                <DatePicker
                  value={String(field.value ?? "")}
                  onChange={field.onChange}
                  onBlur={field.onBlur}
                  name={field.name}
                />
              )}
            />
          </FormField>
          {fields.map((f) => {
            if (!isShown(f, watched)) return null;
            if (f.kind === "party") {
              return (
                <FormField key={f.key} label={f.label} className="sm:col-span-2">
                  <PartyCombobox
                    module={f.module}
                    valueName={(form.watch("party_name") as string) || undefined}
                    onChange={(party) => {
                      form.setValue("party_name", party?.name || "");
                      form.setValue("party_id", party?.id || "");
                    }}
                  />
                </FormField>
              );
            }
            if (f.kind === "money") {
              return (
                <FormField key={f.key} label={f.label}>
                  <MoneyInput {...form.register(f.key as keyof Values)} />
                </FormField>
              );
            }
            if (f.kind === "date") {
              return (
                <FormField key={f.key} label={f.label}>
                  <Controller
                    control={form.control}
                    name={f.key as keyof Values}
                    render={({ field }) => (
                      <DatePicker
                        value={String(field.value || "")}
                        onChange={field.onChange}
                        onBlur={field.onBlur}
                        name={field.name}
                      />
                    )}
                  />
                </FormField>
              );
            }
            if (f.kind === "cash_box") {
              return (
                <FormField
                  key={f.key}
                  label={f.label}
                  hint="صندوق محل پرداخت/دریافت نقد"
                  error={
                    !(cashBoxesQ.data ?? []).length
                      ? "صندوقی تعریف نشده — از خزانه صندوق بسازید"
                      : undefined
                  }
                >
                  <Select {...form.register(f.key as keyof Values)}>
                    <option value="">انتخاب صندوق…</option>
                    {(cashBoxesQ.data ?? []).map((c) => (
                      <option key={c.id} value={c.id}>
                        {c.code} — {c.name}
                      </option>
                    ))}
                  </Select>
                </FormField>
              );
            }
            if (f.kind === "bank_account") {
              return (
                <FormField
                  key={f.key}
                  label={f.label}
                  hint="شماره حساب بانکی مبدأ/مقصد"
                  error={
                    !(bankAccountsQ.data ?? []).length
                      ? "حساب بانکی تعریف نشده — از خزانه حساب بانکی بسازید"
                      : undefined
                  }
                >
                  <Select {...form.register(f.key as keyof Values)}>
                    <option value="">انتخاب حساب بانکی…</option>
                    {(bankAccountsQ.data ?? []).map((b) => (
                      <option key={b.id} value={b.id}>
                        {(bankNameById.get(b.bank_id) || "بانک") + " — " + b.account_number}
                      </option>
                    ))}
                  </Select>
                </FormField>
              );
            }
            if (f.kind === "select") {
              return (
                <FormField key={f.key} label={f.label}>
                  <Select
                    {...form.register(f.key as keyof Values)}
                    onChange={(e) => {
                      form.setValue(f.key as keyof Values, e.target.value as never);
                      // Clear dependent treasury fields when method changes
                      if (f.key === "method") {
                        form.setValue("cash_box_id" as never, "" as never);
                        form.setValue("bank_account_id" as never, "" as never);
                      }
                    }}
                  >
                    <option value="">انتخاب…</option>
                    {(f.options ?? []).map((o) => (
                      <option key={o.value} value={o.value}>
                        {o.label}
                      </option>
                    ))}
                  </Select>
                </FormField>
              );
            }
            return (
              <FormField key={f.key} label={f.label}>
                <Input {...form.register(f.key as keyof Values)} />
              </FormField>
            );
          })}
          <FormField label="توضیحات" className="sm:col-span-2">
            <Input {...form.register("description")} />
          </FormField>
          <div className="flex justify-end gap-2 sm:col-span-2">
            <Button type="button" variant="outline" onClick={() => setOpen(false)}>
              انصراف
            </Button>
            <Button type="submit" disabled={createM.isPending}>
              ذخیره
            </Button>
          </div>
        </form>
      </Dialog>
    </div>
  );
}
