"use client";

import { InventoryLotLineResult } from "@/app/api/inventory";
import {
  fetchInventoryListFresh,
  fetchStockIssueBadItemLotLinesFresh,
  updateInventoryLotLineStatus,
} from "@/app/api/inventory/actions";
import { handleBadItem } from "@/app/api/stockIssue/actions";
import { arrayToDateString } from "@/app/utils/formatUtil";
import { msg, msgError } from "@/components/Swal/CustomAlerts";
import { SessionWithTokens } from "@/config/authConfig";
import {
  Box,
  Button,
  Card,
  CardContent,
  FormControl,
  InputLabel,
  MenuItem,
  Paper,
  Select,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TablePagination,
  TableRow,
  TextField,
  Typography,
} from "@mui/material";
import { uniq } from "lodash";
import { useSession } from "next-auth/react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import StockIssueSearchPanel, {
  StockIssueSearchField,
} from "./StockIssueSearchPanel";

const LOT_STATUSES = ["available", "unavailable"] as const;

type SearchQuery = {
  itemCode: string;
  itemName: string;
  itemType: string;
  lotNo: string;
};
type SearchParamNames = keyof SearchQuery;

type RowDraft = {
  status: string;
  badQty: string;
  remarks: string;
};

const normalizeStatus = (status: string | undefined | null): string => {
  const s = status?.toLowerCase() ?? "";
  return LOT_STATUSES.includes(s as (typeof LOT_STATUSES)[number])
    ? s
    : "unavailable";
};

const BadItemHandleForm: React.FC = () => {
  const { t } = useTranslation(["stockIssue", "common"]);
  const { data: session } = useSession() as { data: SessionWithTokens | null };
  const currentUserId = session?.id ? parseInt(session.id) : undefined;

  const [rows, setRows] = useState<InventoryLotLineResult[]>([]);
  const [totalCount, setTotalCount] = useState(0);
  const [paging, setPaging] = useState({ pageNum: 1, pageSize: 20 });
  const [filterArgs, setFilterArgs] = useState<SearchQuery>({
    itemCode: "",
    itemName: "",
    itemType: "All",
    lotNo: "",
  });
  const [drafts, setDrafts] = useState<Record<number, RowDraft>>({});
  const [itemTypeOptions, setItemTypeOptions] = useState<string[]>([]);
  const [loading, setLoading] = useState(false);
  const [submittingIds, setSubmittingIds] = useState<Set<number>>(new Set());
  const rowSubmitInFlightRef = useRef<Set<number>>(new Set());
  const hasSearchedRef = useRef(false);

  const formatItemTypeLabel = useCallback(
    (code: string) => {
      const key = code?.trim();
      if (!key) return code;
      const translated = t(key, { ns: "common", defaultValue: key });
      return translated !== key ? translated : t(key, { defaultValue: key });
    },
    [t],
  );

  const searchFields: StockIssueSearchField<SearchParamNames>[] = useMemo(
    () => [
      { name: "itemCode", label: t("Code"), type: "text" },
      { name: "itemName", label: t("Name"), type: "text" },
      { name: "lotNo", label: t("Lot No."), type: "text" },
      {
        name: "itemType",
        label: t("Type"),
        type: "select",
        options: itemTypeOptions,
        getOptionLabel: formatItemTypeLabel,
      },
    ],
    [t, itemTypeOptions, formatItemTypeLabel],
  );

  useEffect(() => {
    fetchInventoryListFresh()
      .then((list) => {
        if (!list?.length) return;
        setItemTypeOptions(
          uniq(
            list
              .map((row) => row.itemType?.trim())
              .filter((type): type is string => Boolean(type)),
          ).sort(),
        );
      })
      .catch((e) => console.error("Failed to load item types:", e));
  }, []);

  const buildSearchParams = useCallback(
    (query: SearchQuery, page: { pageNum: number; pageSize: number }) => ({
      itemCode: query.itemCode?.trim() || undefined,
      itemName: query.itemName?.trim() || undefined,
      lotNo: query.lotNo?.trim() || undefined,
      itemType:
        !query.itemType || query.itemType === "All"
          ? undefined
          : query.itemType,
      pageNum: page.pageNum - 1,
      pageSize: page.pageSize,
    }),
    [],
  );

  const applyDraftsForRecords = useCallback(
    (records: InventoryLotLineResult[], replaceAll: boolean) => {
      setDrafts((prev) => {
        const next: Record<number, RowDraft> = replaceAll ? {} : { ...prev };
        records.forEach((line) => {
          if (next[line.id]) return;
          const max = Math.max(0, Math.floor(line.availableQty ?? 0));
          next[line.id] = {
            status: normalizeStatus(line.status),
            badQty: max > 0 ? String(max) : "",
            remarks: "",
          };
        });
        return next;
      });
    },
    [],
  );

  const fetchRows = useCallback(
    async (
      query: SearchQuery,
      page: { pageNum: number; pageSize: number },
      options?: { replaceDrafts?: boolean },
    ) => {
      setLoading(true);
      try {
        const res = await fetchStockIssueBadItemLotLinesFresh(
          buildSearchParams(query, page),
        );
        const records = res?.records ?? [];
        setRows(records);
        setTotalCount(res?.total ?? 0);
        applyDraftsForRecords(records, options?.replaceDrafts ?? false);
      } catch (e) {
        console.error(e);
        setRows([]);
        setTotalCount(0);
        setDrafts({});
      } finally {
        setLoading(false);
      }
    },
    [buildSearchParams, applyDraftsForRecords],
  );

  const handleSearch = useCallback(
    async (query: Record<SearchParamNames, string>) => {
      const q = query as SearchQuery;
      const hasCriterion =
        Boolean(q.itemCode?.trim()) ||
        Boolean(q.itemName?.trim()) ||
        Boolean(q.lotNo?.trim());
      if (!hasCriterion) {
        msgError(t("Please set at least one search criterion"));
        return;
      }

      hasSearchedRef.current = true;
      setFilterArgs(q);
      const page = { pageNum: 1, pageSize: paging.pageSize };
      setPaging(page);
      await fetchRows(q, page, { replaceDrafts: true });
    },
    [fetchRows, paging.pageSize, t],
  );

  useEffect(() => {
    if (!hasSearchedRef.current) return;
    fetchRows(filterArgs, paging, { replaceDrafts: false });
    // eslint-disable-next-line react-hooks/exhaustive-deps -- refetch when paging changes only
  }, [paging.pageNum, paging.pageSize]);

  const formatStatusLabel = useCallback(
    (status: string) => {
      const key = status?.toLowerCase();
      if (key === "available") return t("available");
      if (key === "unavailable") return t("unavailable");
      return status;
    },
    [t],
  );

  const handleRowSubmit = useCallback(
    async (line: InventoryLotLineResult) => {
      if (!currentUserId) return;
      if (rowSubmitInFlightRef.current.has(line.id)) return;

      const draft = drafts[line.id] ?? {
        status: normalizeStatus(line.status),
        badQty: "",
        remarks: "",
      };
      const savedStatus = normalizeStatus(line.status);
      const draftStatus = normalizeStatus(draft.status);
      const statusChanged = draftStatus !== savedStatus;
      const maxQty = Math.max(0, Math.floor(line.availableQty ?? 0));
      const parsed = parseInt((draft.badQty ?? "").replace(/\D/g, ""), 10);
      const hasBadQty = !Number.isNaN(parsed) && parsed >= 1;

      if (!statusChanged && !hasBadQty) {
        msgError(t("No changes to submit"));
        return;
      }
      if (hasBadQty && parsed > maxQty) {
        msgError(t("Quantity exceeds available quantity"));
        return;
      }

      rowSubmitInFlightRef.current.add(line.id);
      setSubmittingIds((prev) => new Set(prev).add(line.id));
      try {
        if (statusChanged) {
          const statusRes = await updateInventoryLotLineStatus({
            inventoryLotLineId: line.id,
            status: draftStatus,
          });
          if (statusRes?.code && statusRes.code !== "SUCCESS") {
            throw new Error(statusRes.message ?? t("Failed to submit"));
          }
        }

        if (hasBadQty) {
          const badRes = await handleBadItem({
            inventoryLotLineId: line.id,
            qty: parsed,
            remarks: draft.remarks?.trim() || undefined,
            handler: currentUserId,
          });
          if (badRes?.code && badRes.code !== "SUCCESS") {
            throw new Error(badRes.message || t("Failed to submit"));
          }
        }

        msg(t("Saved successfully"));

        setRows((prev) => {
          let next = prev.map((row) => {
            if (row.id !== line.id) return row;
            let updated = { ...row, status: draftStatus };
            if (hasBadQty) {
              updated = {
                ...updated,
                availableQty: Math.max(0, (updated.availableQty ?? 0) - parsed),
              };
            }
            return updated;
          });
          if (hasBadQty) {
            next = next.filter((row) => Math.floor(row.availableQty ?? 0) > 0);
          }
          return next;
        });

        setDrafts((prev) => {
          const next = { ...prev };
          if (hasBadQty && Math.max(0, maxQty - parsed) <= 0) {
            delete next[line.id];
          } else if (next[line.id]) {
            next[line.id] = {
              ...next[line.id],
              status: draftStatus,
              remarks: hasBadQty ? "" : next[line.id].remarks,
              badQty: hasBadQty
                ? String(Math.max(0, maxQty - parsed))
                : next[line.id].badQty,
            };
          }
          return next;
        });
      } catch (e: unknown) {
        msgError(e instanceof Error ? e.message : t("Failed to submit"));
      } finally {
        rowSubmitInFlightRef.current.delete(line.id);
        setSubmittingIds((prev) => {
          const next = new Set(prev);
          next.delete(line.id);
          return next;
        });
      }
    },
    [currentUserId, drafts, t],
  );

  const updateDraft = useCallback((lineId: number, patch: Partial<RowDraft>) => {
    setDrafts((prev) => {
      const current = prev[lineId] ?? {
        status: "available",
        badQty: "",
        remarks: "",
      };
      return {
        ...prev,
        [lineId]: { ...current, ...patch },
      };
    });
  }, []);

  return (
    <Box>
      <StockIssueSearchPanel
        fields={searchFields}
        onSearch={handleSearch}
        disabled={loading}
      />

      <Card elevation={0} sx={{ mt: 2 }}>
        <CardContent sx={{ p: 0 }}>
          <Typography variant="overline" sx={{ px: 2, pt: 2, display: "block" }}>
            {t("Bad Item Handle")}
          </Typography>
          <TableContainer component={Paper} elevation={0}>
            <Table size="small">
              <TableHead>
                <TableRow>
                  <TableCell>{t("Code")}</TableCell>
                  <TableCell>{t("Name")}</TableCell>
                  <TableCell>{t("Lot No.")}</TableCell>
                  <TableCell align="right">{t("Available Qty")}</TableCell>
                  <TableCell>{t("Stock UoM")}</TableCell>
                  <TableCell>{t("Expiry Date")}</TableCell>
                  <TableCell>{t("Warehouse")}</TableCell>
                  <TableCell sx={{ minWidth: 140 }}>{t("Status")}</TableCell>
                  <TableCell sx={{ minWidth: 100 }}>{t("Defective Qty")}</TableCell>
                  <TableCell sx={{ minWidth: 120 }}>{t("Remarks")}</TableCell>
                  <TableCell align="center" sx={{ minWidth: 100 }}>
                    {t("Action")}
                  </TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {!hasSearchedRef.current ? (
                  <TableRow>
                    <TableCell colSpan={11} align="center">
                      {t("Search to load lot lines")}
                    </TableCell>
                  </TableRow>
                ) : loading ? (
                  <TableRow>
                    <TableCell colSpan={11} align="center">
                      {t("Loading")}
                    </TableCell>
                  </TableRow>
                ) : rows.length === 0 ? (
                  <TableRow>
                    <TableCell colSpan={11} align="center">
                      {t("No record found")}
                    </TableCell>
                  </TableRow>
                ) : (
                  rows.map((line) => {
                    const maxQty = Math.max(
                      0,
                      Math.floor(line.availableQty ?? 0),
                    );
                    const draft = drafts[line.id] ?? {
                      status: normalizeStatus(line.status),
                      badQty: "",
                      remarks: "",
                    };
                    const isSubmitting = submittingIds.has(line.id);
                    const canSubmit =
                      Boolean(currentUserId) && !isSubmitting;

                    return (
                      <TableRow key={line.id} hover>
                        <TableCell>{line.item?.code ?? "—"}</TableCell>
                        <TableCell>{line.item?.name ?? "—"}</TableCell>
                        <TableCell>{line.lotNo ?? "—"}</TableCell>
                        <TableCell align="right">{maxQty}</TableCell>
                        <TableCell>{line.uom ?? "—"}</TableCell>
                        <TableCell>
                          {arrayToDateString(line.expiryDate)}
                        </TableCell>
                        <TableCell>{line.warehouse?.code ?? "—"}</TableCell>
                        <TableCell>
                          <FormControl
                            size="small"
                            fullWidth
                            disabled={isSubmitting}
                          >
                            <InputLabel id={`status-${line.id}`}>
                              {t("Status")}
                            </InputLabel>
                            <Select
                              labelId={`status-${line.id}`}
                              label={t("Status")}
                              value={normalizeStatus(draft.status)}
                              onChange={(e) =>
                                updateDraft(line.id, { status: e.target.value })
                              }
                            >
                              {LOT_STATUSES.map((s) => (
                                <MenuItem key={s} value={s}>
                                  {formatStatusLabel(s)}
                                </MenuItem>
                              ))}
                            </Select>
                          </FormControl>
                        </TableCell>
                        <TableCell>
                          <TextField
                            size="small"
                            fullWidth
                            value={draft.badQty}
                            disabled={!canSubmit || maxQty <= 0}
                            onChange={(e) => {
                              const raw = e.target.value.replace(/\D/g, "");
                              if (raw === "") {
                                updateDraft(line.id, { badQty: "" });
                                return;
                              }
                              const n = parseInt(raw, 10);
                              if (!Number.isNaN(n)) {
                                updateDraft(line.id, {
                                  badQty: String(
                                    Math.min(Math.max(0, n), maxQty),
                                  ),
                                });
                              }
                            }}
                          />
                        </TableCell>
                        <TableCell>
                          <TextField
                            size="small"
                            fullWidth
                            value={draft.remarks}
                            disabled={!canSubmit}
                            onChange={(e) =>
                              updateDraft(line.id, { remarks: e.target.value })
                            }
                          />
                        </TableCell>
                        <TableCell align="center">
                          <Button
                            variant="contained"
                            color="error"
                            size="small"
                            disabled={!canSubmit}
                            onClick={() => handleRowSubmit(line)}
                          >
                            {isSubmitting
                              ? t("Processing...")
                              : t("Submit")}
                          </Button>
                        </TableCell>
                      </TableRow>
                    );
                  })
                )}
              </TableBody>
            </Table>
          </TableContainer>
          <TablePagination
            component="div"
            count={totalCount}
            page={paging.pageNum - 1}
            onPageChange={(_, page) =>
              setPaging((p) => ({ ...p, pageNum: page + 1 }))
            }
            rowsPerPage={paging.pageSize}
            onRowsPerPageChange={(e) =>
              setPaging({ pageNum: 1, pageSize: parseInt(e.target.value, 10) })
            }
            rowsPerPageOptions={[10, 20, 50, 100]}
            labelRowsPerPage={t("Rows per page")}
          />
        </CardContent>
      </Card>
    </Box>
  );
};

export default BadItemHandleForm;
