"use client";

import Link from "next/link";
import { ArrowUpRight, ChevronDown, ChevronRight, RefreshCw, Search } from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import { toast } from "sonner";

import {
  getAdminReconcileJobItems,
  getAdminReconcileJobs,
  postAdminReconcileJob,
} from "@/api/admin-reconcile";
import {
  completeTransferInCredit,
  manuallyProcessTransferOrder,
  reverseTransferOrder,
} from "@/api/admin-wallet";
import { getAdminPlayers } from "@/api/admin-player";
import { AdminDateRangeField } from "@/components/admin/admin-date-range-field";
import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
import { ReconcileJobDetailPanel, type ReconcileItemsFilter } from "@/modules/reconcile/reconcile-job-detail-panel";
import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { PRD_WALLET_WRITE_ANY } from "@/lib/admin-prd";
import { cn } from "@/lib/utils";
import { useAdminProfile } from "@/stores/admin-session";
import { LotteryApiBizError } from "@/types/api/errors";
import type { AdminPlayerRow } from "@/types/api/admin-player";
import type {
  AdminReconcileJobRow,
  AdminReconcileItemsData,
  AdminReconcileJobListData,
} from "@/types/api/admin-reconcile";
import {
  getJobSummaryValue,
  jobStatusLabel,
  renderPeriodRange,
} from "@/modules/reconcile/reconcile-labels";

const MANAGE = ["prd.wallet_reconcile.manage"] as const;
const RECONCILE_TYPE = "wallet_transfer" as const;

export function ReconcileConsole(): React.ReactElement {
  const { t } = useTranslation(["reconcile", "common"]);
  const tRef = useTranslationRef(["reconcile", "common"]);
  const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
  const profile = useAdminProfile();
  const canCreate = adminHasAnyPermission(profile?.permissions, [...MANAGE]);
  const canWriteWallet = adminHasAnyPermission(profile?.permissions, [...PRD_WALLET_WRITE_ANY]);
  const formatTs = useAdminDateTimeFormatter();

  const [jobs, setJobs] = useState<AdminReconcileJobListData | null>(null);
  const [jobsLoading, setJobsLoading] = useState(true);
  const [jobsErr, setJobsErr] = useState<string | null>(null);
  const [page, setPage] = useState(1);
  const [perPage, setPerPage] = useState(10);

  const [selectedJob, setSelectedJob] = useState<AdminReconcileJobRow | null>(null);
  const [items, setItems] = useState<AdminReconcileItemsData | null>(null);
  const [itemsPage, setItemsPage] = useState(1);
  const [itemsPerPage, setItemsPerPage] = useState(20);
  const [itemsLoading, setItemsLoading] = useState(false);
  const [itemsFilter, setItemsFilter] = useState<ReconcileItemsFilter>("open");

  const [scanOpen, setScanOpen] = useState(false);
  const [dateFrom, setDateFrom] = useState("");
  const [dateTo, setDateTo] = useState("");
  const [playerSearch, setPlayerSearch] = useState("");
  const [playerResults, setPlayerResults] = useState<AdminPlayerRow[]>([]);
  const [playerLoading, setPlayerLoading] = useState(false);
  const [playerPickerOpen, setPlayerPickerOpen] = useState(false);
  const [selectedPlayer, setSelectedPlayer] = useState<AdminPlayerRow | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [actionBusy, setActionBusy] = useState(false);

  const loadJobs = useCallback(async () => {
    setJobsLoading(true);
    setJobsErr(null);
    try {
      const d = await getAdminReconcileJobs({ page, per_page: perPage });
      setJobs(d);
    } catch (e) {
      setJobsErr(e instanceof LotteryApiBizError ? e.message : tRef.current("loadFailed"));
      setJobs(null);
    } finally {
      setJobsLoading(false);
    }
  }, [page, perPage, tRef]);

  useAsyncEffect(() => {
    void loadJobs();
  }, [page, perPage]);

  const loadItems = useCallback(async () => {
    if (selectedJob == null) {
      setItems(null);
      return;
    }
    setItemsLoading(true);
    try {
      const d = await getAdminReconcileJobItems(selectedJob.id, {
        page: itemsPage,
        per_page: itemsPerPage,
      });
      setItems(d);
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : tRef.current("loadItemsFailed"));
      setItems(null);
    } finally {
      setItemsLoading(false);
    }
  }, [selectedJob, itemsPage, itemsPerPage, tRef]);

  useAsyncEffect(() => {
    void loadItems();
  }, [selectedJob, itemsPage, itemsPerPage]);

  const loadPlayers = useCallback(async (keyword: string) => {
    const q = keyword.trim();
    if (q === "") {
      setPlayerResults([]);
      return;
    }
    setPlayerLoading(true);
    try {
      const data = await getAdminPlayers({ page: 1, per_page: 8, keyword: q });
      setPlayerResults(data.items);
    } catch {
      setPlayerResults([]);
    } finally {
      setPlayerLoading(false);
    }
  }, []);

  useEffect(() => {
    if (!playerPickerOpen) {
      return;
    }
    const timer = window.setTimeout(() => {
      void loadPlayers(playerSearch);
    }, 250);
    return () => window.clearTimeout(timer);
  }, [loadPlayers, playerPickerOpen, playerSearch]);

  async function onCreate(): Promise<void> {
    if (!dateFrom.trim() || !dateTo.trim()) {
      toast.error(t("periodRequired"));
      return;
    }
    if (dateFrom > dateTo) {
      toast.error(t("periodOrderInvalid"));
      return;
    }

    setSubmitting(true);
    try {
      const resp = await postAdminReconcileJob({
        reconcile_type: RECONCILE_TYPE,
        date_from: dateFrom,
        date_to: dateTo,
        player_id: selectedPlayer ? selectedPlayer.id : null,
      });
      const count =
        typeof resp.summary_json?.item_count === "number"
          ? resp.summary_json.item_count
          : resp.item_count ?? 0;
      toast.success(count > 0 ? t("createSuccess", { count }) : t("createSuccessEmpty"));
      setPage(1);
      setScanOpen(false);
      setDateFrom("");
      setDateTo("");
      setPlayerSearch("");
      setSelectedPlayer(null);
      setPlayerResults([]);
      await loadJobs();
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("createFailed"));
    } finally {
      setSubmitting(false);
    }
  }

  async function runTransferAction(
    transferNo: string,
    action: "reverse" | "complete_credit" | "manually_process",
  ): Promise<void> {
    const confirmKey =
      action === "reverse"
        ? "reverse"
        : action === "complete_credit"
          ? "completeCredit"
          : "markCaseClosed";
    const successKey =
      action === "reverse"
        ? "reverseSuccess"
        : action === "complete_credit"
          ? "completeCreditSuccess"
          : "markCaseClosedSuccess";

    requestConfirm({
      title: t(`confirm.${confirmKey}Title`, { ns: "wallet" }),
      description: t(`confirm.${confirmKey}Description`, { ns: "wallet", transferNo }),
      onConfirm: async () => {
        setActionBusy(true);
        try {
          if (action === "reverse") {
            await reverseTransferOrder(transferNo);
          } else if (action === "complete_credit") {
            await completeTransferInCredit(transferNo);
          } else {
            await manuallyProcessTransferOrder(transferNo);
          }
          toast.success(t(successKey, { ns: "wallet" }));
          await loadItems();
          await loadJobs();
        } catch (e) {
          toast.error(e instanceof LotteryApiBizError ? e.message : t("actionFailed", { ns: "wallet" }));
        } finally {
          setActionBusy(false);
        }
      },
    });
  }

  function openJobDetail(job: AdminReconcileJobRow): void {
    setSelectedJob(job);
    setItemsPage(1);
    setItemsFilter("open");
  }

  function closeJobDetail(): void {
    setSelectedJob(null);
    setItems(null);
  }

  const jm = jobs?.meta;

  return (
    <div className="flex w-full flex-col gap-4">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <p className="text-sm text-muted-foreground">{t("workflowHint")}</p>
        <Link
          href="/admin/wallet/transfer-orders?abnormal=1"
          title={t("shortcutAbnormalTransfers")}
          className="inline-flex shrink-0 items-center gap-1 text-sm font-medium hover:underline"
        >
          {t("shortcutAbnormalTransfers")}
          <ArrowUpRight className="size-3.5" />
        </Link>
      </div>

      {canCreate ? (
        <div className="rounded-lg border border-border/60">
          <button
            type="button"
            className="flex w-full items-center justify-between px-3 py-2.5 text-left text-sm font-medium"
            onClick={() => setScanOpen((open) => !open)}
          >
            {t("createTitle")}
            {scanOpen ? <ChevronDown className="size-4 text-muted-foreground" /> : <ChevronRight className="size-4 text-muted-foreground" />}
          </button>
          {scanOpen ? (
            <div className="space-y-3 border-t border-border/60 px-3 py-3">
              <div className="flex flex-wrap items-end gap-2">
                <div className="min-w-[14rem] flex-1">
                  <AdminDateRangeField
                    id="rc-date-range"
                    label={t("dateRange")}
                    from={dateFrom}
                    to={dateTo}
                    onRangeChange={({ from, to }) => {
                      setDateFrom(from);
                      setDateTo(to);
                    }}
                  />
                </div>
                <div className="min-w-[12rem] flex-1">
                  <Label htmlFor="rc-player-search" className="mb-1.5 block text-sm">
                    {t("playerSearch")}
                  </Label>
                  <Popover open={playerPickerOpen} onOpenChange={setPlayerPickerOpen} modal={false}>
                    <div className="flex gap-1.5">
                      <Input
                        id="rc-player-search"
                        className="h-8"
                        value={playerSearch}
                        onChange={(e) => setPlayerSearch(e.target.value)}
                        placeholder={t("playerSearchPlaceholder")}
                      />
                      <PopoverTrigger
                        render={
                          <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            className="h-8 shrink-0 px-2"
                            aria-label={t("searchPicker.open", { ns: "reports", defaultValue: "搜索" })}
                          />
                        }
                      >
                        <Search className="size-3.5" />
                      </PopoverTrigger>
                    </div>
                    <PopoverContent align="start" className="w-[var(--anchor-width)] min-w-[16rem] p-2">
                      <div className="max-h-48 overflow-y-auto">
                        {playerLoading ? (
                          <AdminLoadingInline className="py-2" label={t("loadingPlayers")} />
                        ) : playerResults.length === 0 ? (
                          <p className="px-2 py-3 text-sm text-muted-foreground">{t("playerEmpty", { defaultValue: "无匹配玩家" })}</p>
                        ) : (
                          playerResults.map((player) => (
                            <button
                              key={player.id}
                              type="button"
                              className="flex w-full rounded-md px-2 py-2 text-left text-sm hover:bg-muted"
                              onClick={() => {
                                setSelectedPlayer(player);
                                setPlayerSearch(player.site_player_id);
                                setPlayerPickerOpen(false);
                              }}
                            >
                              <span className="truncate">
                                {player.site_player_id}
                                {player.nickname ? ` · ${player.nickname}` : ""}
                              </span>
                            </button>
                          ))
                        )}
                      </div>
                    </PopoverContent>
                  </Popover>
                </div>
                <Button
                  type="button"
                  size="sm"
                  className="h-8"
                  disabled={submitting}
                  onClick={() =>
                    requestConfirm({
                      title: t("confirmCreateTitle"),
                      description: t("confirmCreateDescription", {
                        playerHint: selectedPlayer
                          ? t("confirmCreatePlayer")
                          : t("confirmCreateAllPlayers"),
                      }),
                      onConfirm: () => onCreate(),
                    })
                  }
                >
                  {submitting ? t("submitting") : t("createTask")}
                </Button>
              </div>
              {selectedPlayer ? (
                <div className="flex items-center justify-between gap-2 rounded-md bg-muted/40 px-2.5 py-1.5 text-sm">
                  <span className="min-w-0 truncate">
                    {selectedPlayer.site_player_id}
                    {selectedPlayer.nickname ? ` · ${selectedPlayer.nickname}` : ""}
                  </span>
                  <Button
                    type="button"
                    size="sm"
                    variant="ghost"
                    className="h-7 px-2"
                    onClick={() => {
                      setSelectedPlayer(null);
                      setPlayerSearch("");
                    }}
                  >
                    {t("playerClear")}
                  </Button>
                </div>
              ) : null}
            </div>
          ) : null}
        </div>
      ) : (
        <p className="text-sm text-muted-foreground">{t("viewOnlyHint")}</p>
      )}

      <div className="rounded-lg border border-border/60">
        <div className="flex items-center justify-between gap-3 border-b border-border/60 px-3 py-2.5">
          <h2 className="text-sm font-semibold">{t("jobsTitle")}</h2>
          <Button type="button" variant="ghost" size="sm" className="h-8" disabled={jobsLoading} onClick={() => void loadJobs()}>
            <RefreshCw className={cn("size-3.5", jobsLoading && "animate-spin")} />
            {t("refresh")}
          </Button>
        </div>
        <div className="p-3 pt-2">
          {jobsErr ? <p className="mb-2 text-sm text-destructive">{jobsErr}</p> : null}
          {jobs ? (
            <>
              <Table id="reconcile-jobs-table">
                <TableHeader>
                  <TableRow>
                    <TableHead>{t("jobNo")}</TableHead>
                    <TableHead>{t("status")}</TableHead>
                    <TableHead className="text-center">{t("mismatchCount")}</TableHead>
                    <TableHead>{t("period")}</TableHead>
                    <TableHead>{t("finishedAt")}</TableHead>
                    <TableHead className="w-[6.5rem] text-center">{t("operate")}</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {jobsLoading && jobs.items.length === 0 ? (
                    <AdminTableLoadingRow colSpan={6} />
                  ) : jobs.items.length === 0 ? (
                    <AdminTableNoResourceRow colSpan={6} />
                  ) : (
                    jobs.items.map((row) => {
                      const selected = selectedJob?.id === row.id;
                      const mismatchCount = getJobSummaryValue(row.summary_json, "mismatch_count");
                      return (
                        <TableRow key={row.id} className={cn(selected && "bg-primary/[0.04]")}>
                          <TableCell className="font-mono text-xs">{row.job_no}</TableCell>
                          <TableCell>
                            <AdminStatusBadge status={row.status}>
                              {jobStatusLabel(row.status, t)}
                            </AdminStatusBadge>
                          </TableCell>
                          <TableCell className="text-center tabular-nums">
                            <span
                              className={cn(
                                mismatchCount > 0 ? "font-medium text-amber-700" : "text-muted-foreground",
                              )}
                            >
                              {mismatchCount}
                            </span>
                          </TableCell>
                          <TableCell className="max-w-[14rem] text-xs text-muted-foreground">
                            {renderPeriodRange(row, formatTs)}
                          </TableCell>
                          <TableCell className="whitespace-nowrap text-xs text-muted-foreground">
                            {formatTs(row.finished_at)}
                          </TableCell>
                          <TableCell className="text-center">
                            <Button
                              type="button"
                              size="sm"
                              variant={selected ? "default" : "outline"}
                              className="h-8"
                              onClick={() => openJobDetail(row)}
                            >
                              {t("viewDetails")}
                            </Button>
                          </TableCell>
                        </TableRow>
                      );
                    })
                  )}
                </TableBody>
              </Table>
              {jm ? (
                <div className="mt-3 border-t border-border/60 pt-2">
                  <AdminListPaginationFooter
                    selectId="reconcile-jobs-per-page"
                    total={jm.total}
                    page={jm.current_page}
                    lastPage={Math.max(1, jm.last_page)}
                    perPage={jm.per_page}
                    loading={jobsLoading}
                    onPerPageChange={(n) => {
                      setPerPage(n);
                      setPage(1);
                    }}
                    onPageChange={setPage}
                  />
                </div>
              ) : null}
            </>
          ) : jobsLoading ? (
            <AdminNoResourceState message={t("loadFailed")} />
          ) : null}
        </div>
      </div>

      <Dialog
        open={selectedJob != null}
        onOpenChange={(open) => {
          if (!open) {
            closeJobDetail();
          }
        }}
      >
        <DialogContent
          className="flex max-h-[min(90vh,48rem)] w-full max-w-[min(72rem,calc(100%-2rem))] flex-col gap-0 overflow-hidden p-0 sm:max-w-[min(72rem,calc(100%-2rem))]"
          showCloseButton
        >
          {selectedJob ? (
            <ReconcileJobDetailPanel
              job={selectedJob}
              items={items}
              itemsLoading={itemsLoading}
              itemsFilter={itemsFilter}
              onItemsFilterChange={setItemsFilter}
              canWriteWallet={canWriteWallet}
              actionBusy={actionBusy}
              onCompleteCredit={(transferNo) => void runTransferAction(transferNo, "complete_credit")}
              onReverse={(transferNo) => void runTransferAction(transferNo, "reverse")}
              onManualProcess={(transferNo) => void runTransferAction(transferNo, "manually_process")}
              onItemsPageChange={setItemsPage}
              onItemsPerPageChange={setItemsPerPage}
            />
          ) : null}
        </DialogContent>
      </Dialog>

      <ConfirmDialog />
    </div>
  );
}