"use client";

/**
 * Retiring an account from inside the member module.
 *
 * The same action, dialog and endpoint as the Account Deletion page — this is an
 * entry point, not a second implementation. Someone looking at a member's record is
 * already holding the context the deletion needs, and making them copy the
 * membership number into another screen to act on it invites copying it wrong.
 *
 * Once an account has been retired this stops offering the action and shows a badge
 * with the countdown to the data-deletion email instead. Only the countdown: the
 * Deletion tab on this same page carries the full history — every round, both
 * emails, the reason, the reactivate button — and duplicating that here would be two
 * places to keep true about one record.
 *
 * Super admin only, and core enforces that on every endpoint behind it. The page
 * decides whether to render this at all, from its loader.
 */

import { Badge, Button, Loader, Tooltip } from "@mantine/core";
import { notifications } from "@mantine/notifications";
import { IconClock, IconUserOff } from "@tabler/icons-react";
import { useCallback, useEffect, useState } from "react";
import {
  applyAccountDeletion,
  getDeletionConfig,
  listDeletionRecords,
  listDeletionStatuses,
  searchAccounts,
  type AccountSearchResult,
  type DeletionConfig,
  type DeletionRecord,
  type DeletionStatus,
} from "@/lib/features/account-deletion/query";
import { listEDMTemplates } from "@/lib/features/edm/query";
import DeleteAccountModal, {
  type DeletionRequest,
  type TemplateOption,
} from "@/routes/account-deletion/_components/DeleteAccountModal";

/**
 * Whole days until the follow-up, or null once it is no longer pending.
 *
 * Rounded up, not down: a mail due in eleven hours is "1 day", because rounding it
 * to 0 reads as "today" and someone will act on that.
 */
const daysUntil = (iso: string | null): number | null => {
  if (!iso) return null;
  const due = new Date(iso).getTime();
  if (!Number.isFinite(due)) return null;
  return Math.max(0, Math.ceil((due - Date.now()) / (24 * 60 * 60 * 1000)));
};

const formatDay = (value: string | null): string => {
  if (!value) return "—";
  const parsed = new Date(value);
  return Number.isFinite(parsed.getTime()) ? parsed.toLocaleDateString() : "—";
};

const DeleteAccountControl: React.FC<{ membershipNumber?: string }> = ({
  membershipNumber,
}) => {
  const [record, setRecord] = useState<DeletionRecord | null>(null);
  const [loading, setLoading] = useState(true);

  const [account, setAccount] = useState<AccountSearchResult | null>(null);
  const [opening, setOpening] = useState(false);
  const [submitting, setSubmitting] = useState(false);

  const [statuses, setStatuses] = useState<DeletionStatus[]>([]);
  const [missingStatusIDs, setMissingStatusIDs] = useState<string[]>([]);
  const [templates, setTemplates] = useState<TemplateOption[]>([]);
  const [config, setConfig] = useState<DeletionConfig | null>(null);

  /*
   * The account's own deletion history, newest first, narrowed to one row.
   *
   * Searched by membership number rather than fetched by id because the record is
   * keyed by its own uuid, which this page has no way to know. An account can have
   * several records — retired, reactivated, retired again — and only the latest
   * describes the state it is in now.
   */
  const loadRecord = useCallback(async () => {
    if (!membershipNumber) {
      setLoading(false);
      return;
    }
    const response = await listDeletionRecords({
      page: 1,
      pageSize: 1,
      search: membershipNumber,
    });
    if (response.success && response.data) {
      const latest = response.data.items[0] ?? null;
      // The search is an ILIKE across several columns, so a different account whose
      // name happens to contain this number could come back. Only an exact match on
      // the account number describes *this* account.
      setRecord(
        latest?.accountNumber === membershipNumber ? latest : null,
      );
    }
    setLoading(false);
  }, [membershipNumber]);

  useEffect(() => {
    void loadRecord();
  }, [loadRecord]);

  /*
   * Everything the dialog needs, fetched on the click rather than on mount.
   *
   * Four requests — the account as both systems see it, the statuses, the EDM list
   * and the configured window — and almost nobody who opens a member page is about
   * to delete it. Paying for them up front would slow every visit for the rare one
   * that acts.
   */
  const openDialog = useCallback(async () => {
    if (!membershipNumber) return;
    setOpening(true);
    const [accountResponse, statusResponse, templateResponse, configResponse] =
      await Promise.all([
        searchAccounts({ accountNumber: membershipNumber }),
        listDeletionStatuses(),
        listEDMTemplates(),
        getDeletionConfig(),
      ]);
    setOpening(false);

    if (!accountResponse.success || !accountResponse.data?.items.length) {
      notifications.show({
        color: "red",
        message: accountResponse.message || "Could not load the account",
      });
      return;
    }

    if (statusResponse.success && statusResponse.data) {
      setStatuses(statusResponse.data.items);
      setMissingStatusIDs(statusResponse.data.missingExtStatusIDs ?? []);
    }
    if (templateResponse.success && Array.isArray(templateResponse.data)) {
      setTemplates(
        templateResponse.data.map((template) => ({
          id: template.id,
          name: template.name,
        })),
      );
    }
    if (configResponse.success && configResponse.data) {
      setConfig(configResponse.data);
    }
    setAccount(accountResponse.data.items[0] ?? null);
  }, [membershipNumber]);

  const confirmDeletion = useCallback(
    async (request: DeletionRequest) => {
      if (!account) return;
      setSubmitting(true);
      const response = await applyAccountDeletion({
        accountNumber: account.accountNumber,
        ...request,
      });
      setSubmitting(false);

      const partial = response.data?.localError;
      if (partial) {
        notifications.show({
          color: "orange",
          autoClose: false,
          title: "Partly applied",
          message: `Viewpoint was updated. The member database was not: ${partial}`,
        });
      } else if (!response.success) {
        notifications.show({
          color: "red",
          message: response.message || "Failed to delete the account",
        });
        return;
      } else {
        notifications.show({
          color: "green",
          // Correcting an existing closure queues no acknowledgement — see the
          // deletion page's copy of this for why the two are worded apart.
          message: response.data?.updatedExisting
            ? `Account ${account.accountNumber} corrected to ${response.data?.appliedStatus.name} — existing closure updated, no new email sent`
            : `Account ${account.accountNumber} moved to ${response.data?.appliedStatus.name}`,
        });
      }

      if (response.data?.viewpointLogError) {
        notifications.show({
          color: "orange",
          autoClose: false,
          title: "Viewpoint log note not written",
          message: response.data.viewpointLogError,
        });
      }

      setAccount(null);
      await loadRecord();
    },
    [account, loadRecord],
  );

  // Visibility is the page's decision — it has the loader's answer, which is the
  // same on the server and the client.
  if (!membershipNumber) return null;
  if (loading) return <Loader size="xs" />;

  const active = record && !record.reactivatedAt;
  const remaining =
    active && record.followupEdmStatus === "pending"
      ? daysUntil(record.followupDueAt)
      : null;
  return (
    <>
      {active ? (
        /*
          A compact statement of state, not a second detail view.

          The Deletion tab on this same page carries the full history — every round,
          both emails, the reason, the reactivate button. Repeating it in a popover
          here would be two places to keep true about the same record, so this says
          only what someone scanning the header needs: it is closed, and when the
          data-deletion email goes.
        */
        <Tooltip
          label={
            remaining === null
              ? `Retired on ${formatDay(record.createdAt)} — see the Deletion tab`
              : `Data-deletion email due ${formatDay(record.followupDueAt)} — see the Deletion tab`
          }
          multiline
          w={260}
        >
          <Badge
            color="red"
            variant="light"
            leftSection={<IconClock size={12} />}
          >
            {remaining === null
              ? "Deleted"
              : `Deleted — data email in ${remaining}d`}
          </Badge>
        </Tooltip>
      ) : (
        <Button
          size="compact-sm"
          color="red"
          variant="light"
          leftSection={<IconUserOff size={14} />}
          loading={opening}
          onClick={() => void openDialog()}
        >
          Delete account
        </Button>
      )}

      <DeleteAccountModal
        opened={Boolean(account)}
        account={account}
        statuses={statuses}
        missingStatusIDs={missingStatusIDs}
        statusesLoading={false}
        submitting={submitting}
        templates={templates}
        templatesLoading={false}
        config={config}
        onClose={() => setAccount(null)}
        onConfirm={confirmDeletion}
      />
    </>
  );
};

export default DeleteAccountControl;
