import { useState } from "react";
import {
  Alert,
  Badge,
  Button,
  Card,
  Group,
  Loader,
  Modal,
  SimpleGrid,
  Stack,
  Table,
  Text,
  Textarea,
  Title,
  Tooltip,
} from "@mantine/core";
import { IconArrowBackUp, IconInfoCircle, IconTrash } from "@tabler/icons-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useNavigate, useParams } from "react-router-dom";

import { useAuth } from "@/auth/AuthContext";
import { FINANCE_PERMS } from "@/auth/permissions";
import { PageHeader } from "@/shared/components/PageHeader";
import { ApiErrorAlert } from "@/shared/components/ApiErrorAlert";
import { formatMoney } from "@/shared/lib/formatMoney";
import { discardJournal, fetchJournal, postJournal, reverseJournal } from "./api";
import { STATUS_COLOR } from "./types";

function Field({ label, value }: { label: string; value: React.ReactNode }) {
  return (
    <Stack gap={2}>
      <Text size="xs" c="dimmed" tt="uppercase">
        {label}
      </Text>
      <Text size="sm">{value ?? "—"}</Text>
    </Stack>
  );
}

export function JournalDetailPage() {
  const { id = "" } = useParams();
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const { can } = useAuth();

  const [reverseOpen, setReverseOpen] = useState(false);
  const [reason, setReason] = useState("");
  const [actionError, setActionError] = useState<unknown>(null);

  const entry = useQuery({
    queryKey: ["journals", id],
    queryFn: () => fetchJournal(id),
    enabled: Boolean(id),
  });

  const invalidate = () => {
    void queryClient.invalidateQueries({ queryKey: ["journals"] });
  };

  const post = useMutation({
    mutationFn: () => postJournal(id),
    onMutate: () => setActionError(null),
    onSuccess: invalidate,
    onError: setActionError,
  });

  const reverse = useMutation({
    mutationFn: () => reverseJournal(id, { reason }),
    onMutate: () => setActionError(null),
    onSuccess: (created) => {
      invalidate();
      setReverseOpen(false);
      setReason("");
      navigate(`/journals/${created.id}`);
    },
    onError: setActionError,
  });

  const discard = useMutation({
    mutationFn: () => discardJournal(id),
    onMutate: () => setActionError(null),
    onSuccess: () => {
      invalidate();
      navigate("/journals");
    },
    onError: setActionError,
  });

  if (entry.isLoading) {
    return (
      <Group justify="center" py="xl">
        <Loader />
      </Group>
    );
  }
  if (entry.error || !entry.data) {
    return <ApiErrorAlert error={entry.error} title="Could not load the entry" />;
  }

  const data = entry.data;
  const isDraft = data.status === "DRAFT";
  const isPosted = data.status === "POSTED";

  return (
    <>
      <PageHeader
        title={data.entryNumber}
        description={data.memo}
        actions={
          <Group gap="sm">
            <Button variant="default" onClick={() => navigate("/journals")}>
              Back
            </Button>

            {isDraft && can(FINANCE_PERMS.journal.create) && (
              <Button
                variant="default"
                color="red"
                leftSection={<IconTrash size={16} />}
                loading={discard.isPending}
                onClick={() => discard.mutate()}
              >
                Discard
              </Button>
            )}

            {/* Posting is a separate permission from preparing — a segregation
                of duty, so the reason for a missing button is spelled out
                rather than left as an absence. */}
            {isDraft &&
              (can(FINANCE_PERMS.journal.post) ? (
                <Button loading={post.isPending} onClick={() => post.mutate()}>
                  Post to ledger
                </Button>
              ) : (
                <Tooltip label="Posting is a separate approval from preparing. Ask someone who holds it.">
                  <Button disabled>Post to ledger</Button>
                </Tooltip>
              ))}

            {isPosted && can(FINANCE_PERMS.journal.reverse) && (
              <Button
                variant="light"
                color="orange"
                leftSection={<IconArrowBackUp size={16} />}
                onClick={() => setReverseOpen(true)}
              >
                Reverse
              </Button>
            )}
          </Group>
        }
      />

      <ApiErrorAlert error={actionError} title="The ledger refused that" />

      {data.status === "REVERSED" && (
        <Alert
          icon={<IconInfoCircle size={18} />}
          color="orange"
          mb="md"
          title="This entry has been reversed"
        >
          Its effect has been undone by a mirrored entry. Both remain in the
          ledger — that is the audit trail.{" "}
          {data.reversedByEntryId && (
            <Text
              component="span"
              td="underline"
              style={{ cursor: "pointer" }}
              onClick={() => navigate(`/journals/${data.reversedByEntryId}`)}
            >
              Open the reversal
            </Text>
          )}
        </Alert>
      )}

      {data.reversesEntryId && (
        <Alert icon={<IconInfoCircle size={18} />} color="blue" mb="md">
          This is a reversing entry.{" "}
          <Text
            component="span"
            td="underline"
            style={{ cursor: "pointer" }}
            onClick={() => navigate(`/journals/${data.reversesEntryId}`)}
          >
            Open the entry it reverses
          </Text>
          {data.reversalReason ? ` — reason: ${data.reversalReason}` : ""}
        </Alert>
      )}

      {isPosted && (
        <Alert color="green" mb="md" variant="light">
          Posted to the ledger. It can no longer be edited or deleted — a
          correction is a reversing entry.
        </Alert>
      )}

      <Card withBorder mb="md" padding="lg">
        <SimpleGrid cols={{ base: 2, sm: 3, md: 5 }} spacing="lg">
          <Field
            label="Status"
            value={
              <Badge size="sm" variant="light" color={STATUS_COLOR[data.status]}>
                {data.status}
              </Badge>
            }
          />
          <Field label="Entry date" value={data.entryDate} />
          <Field label="Type" value={data.journalType} />
          <Field label="Reference" value={data.reference} />
          <Field label="Currency" value={data.currency} />
          <Field
            label="Posted at"
            value={data.postedAt ? new Date(data.postedAt).toLocaleString() : "—"}
          />
          <Field
            label="Source"
            value={
              data.sourceModule ? `${data.sourceModule}:${data.sourceId}` : "Manual"
            }
          />
        </SimpleGrid>
      </Card>

      <Title order={4} mb="xs">
        Lines
      </Title>
      <Table.ScrollContainer minWidth={760}>
        <Table striped withTableBorder>
          <Table.Thead>
            <Table.Tr>
              <Table.Th w={60}>#</Table.Th>
              <Table.Th w={120}>Account</Table.Th>
              <Table.Th>Name</Table.Th>
              <Table.Th>Description</Table.Th>
              <Table.Th w={150} ta="right">
                Debit
              </Table.Th>
              <Table.Th w={150} ta="right">
                Credit
              </Table.Th>
            </Table.Tr>
          </Table.Thead>
          <Table.Tbody>
            {data.lines.map((line) => (
              <Table.Tr key={line.id}>
                <Table.Td>{line.lineNumber}</Table.Td>
                <Table.Td>
                  <Text ff="monospace" size="sm">
                    {line.accountCode}
                  </Text>
                </Table.Td>
                <Table.Td>{line.accountName?.en}</Table.Td>
                <Table.Td>
                  <Text size="sm" c="dimmed">
                    {line.description ?? "—"}
                  </Text>
                </Table.Td>
                {/* A ledger shows the amount in one column and leaves the other
                    blank — never a negative number in both. */}
                <Table.Td ta="right">
                  <Text ff="monospace">
                    {Number(line.debit) > 0 ? formatMoney(line.debit) : ""}
                  </Text>
                </Table.Td>
                <Table.Td ta="right">
                  <Text ff="monospace">
                    {Number(line.credit) > 0 ? formatMoney(line.credit) : ""}
                  </Text>
                </Table.Td>
              </Table.Tr>
            ))}
          </Table.Tbody>
          <Table.Tfoot>
            <Table.Tr>
              <Table.Td colSpan={4}>
                <Text fw={600}>Total</Text>
              </Table.Td>
              <Table.Td ta="right">
                <Text ff="monospace" fw={700}>
                  {formatMoney(data.totalDebit)}
                </Text>
              </Table.Td>
              <Table.Td ta="right">
                <Text ff="monospace" fw={700}>
                  {formatMoney(data.totalCredit)}
                </Text>
              </Table.Td>
            </Table.Tr>
          </Table.Tfoot>
        </Table>
      </Table.ScrollContainer>

      <Modal
        opened={reverseOpen}
        onClose={() => setReverseOpen(false)}
        title={`Reverse ${data.entryNumber}`}
      >
        <Stack>
          <Text size="sm" c="dimmed">
            This writes a NEW entry with the debits and credits swapped, dated
            today. The original stays in the ledger exactly as it is — that pair
            is the audit trail.
          </Text>
          <Textarea
            label="Reason"
            description="Recorded permanently on both entries."
            required
            autosize
            minRows={3}
            value={reason}
            onChange={(event) => setReason(event.currentTarget.value)}
          />
          <Group justify="flex-end">
            <Button variant="default" onClick={() => setReverseOpen(false)}>
              Cancel
            </Button>
            <Button
              color="orange"
              disabled={!reason.trim()}
              loading={reverse.isPending}
              onClick={() => reverse.mutate()}
            >
              Reverse
            </Button>
          </Group>
        </Stack>
      </Modal>
    </>
  );
}
