import {
  Alert,
  Badge,
  Button,
  Card,
  Group,
  Modal,
  NumberInput,
  Select,
  Stack,
  Table,
  Text,
  TextInput,
  Textarea,
  Title,
} from "@mantine/core";
import { IconAlertTriangle, IconArrowLeft, IconUserPlus } from "@tabler/icons-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";

import { Can } from "@/auth/Can";
import { HR_PERMS } from "@/auth/permissions";
import { apiErrorMessage } from "@/auth/http";
import { PageHeader } from "@/shared/components/PageHeader";
import { BilingualTextInput } from "@/shared/components/BilingualTextInput";
import { localized } from "@/shared/lib/localizedName";
import {
  apply,
  getOpening,
  getPipeline,
  listApplications,
  moveStage,
  upsertApplicant,
} from "./api";
import { PipelineBar } from "./components/PipelineBar";
import { OpeningBadge, StageBadge } from "./components/StageBadge";
import { ApplicationDrawer } from "./components/ApplicationDrawer";
import type { ApplicationStage } from "./types";

/** What each stage may become — mirrors the server's state machine exactly. */
const NEXT: Record<ApplicationStage, ApplicationStage[]> = {
  APPLIED: ["SCREENING", "REJECTED"],
  SCREENING: ["SHORTLISTED", "REJECTED"],
  SHORTLISTED: ["INTERVIEW", "OFFER", "REJECTED"],
  INTERVIEW: ["OFFER", "REJECTED"],
  OFFER: ["REJECTED"],
  HIRED: [],
  REJECTED: [],
  WITHDRAWN: [],
};

export function OpeningDetailPage() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const { i18n } = useTranslation();
  const [stageFilter, setStageFilter] = useState<ApplicationStage | null>(null);
  const [addingApplicant, setAddingApplicant] = useState(false);
  const [openApplication, setOpenApplication] = useState<string | null>(null);
  const [rejecting, setRejecting] = useState<string | null>(null);
  const [rejectReason, setRejectReason] = useState("");
  const [error, setError] = useState<string | null>(null);

  const opening = useQuery({
    queryKey: ["job-opening", id],
    queryFn: () => getOpening(id!),
    enabled: Boolean(id),
  });
  const pipeline = useQuery({
    queryKey: ["pipeline", id],
    queryFn: () => getPipeline(id!),
    enabled: Boolean(id),
  });
  const applications = useQuery({
    queryKey: ["applications", id, stageFilter],
    queryFn: () =>
      listApplications({
        jobOpeningId: id!,
        stage: stageFilter ?? undefined,
        limit: 100,
      }),
    enabled: Boolean(id),
  });

  const invalidate = () => {
    queryClient.invalidateQueries({ queryKey: ["applications"] });
    queryClient.invalidateQueries({ queryKey: ["pipeline"] });
    queryClient.invalidateQueries({ queryKey: ["job-opening"] });
  };

  const advance = useMutation({
    mutationFn: (payload: {
      id: string;
      stage: ApplicationStage;
      reason?: string;
    }) => moveStage(payload.id, { stage: payload.stage, reason: payload.reason }),
    onSuccess: () => {
      invalidate();
      setRejecting(null);
      setRejectReason("");
    },
    onError: (err) => setError(apiErrorMessage(err)),
  });

  if (!opening.data) return <Text>Loading…</Text>;
  const data = opening.data;
  const items = applications.data?.items ?? [];

  return (
    <>
      <Button
        variant="subtle"
        size="compact-sm"
        leftSection={<IconArrowLeft size={14} />}
        mb="sm"
        onClick={() => navigate("/recruitment")}
      >
        All vacancies
      </Button>

      <PageHeader
        title={localized(data.title, i18n.language) || data.reference}
        description={`${data.reference} · ${data.filled} of ${data.openings} filled`}
        actions={
          data.status === "OPEN" ? (
            <Can permission={HR_PERMS.recruitment.viewApplication}>
              <Button
                leftSection={<IconUserPlus size={16} />}
                onClick={() => setAddingApplicant(true)}
              >
                Add applicant
              </Button>
            </Can>
          ) : undefined
        }
      />

      <Group mb="md" gap="xs">
        <OpeningBadge status={data.status} />
        {data.closesOn && (
          <Badge variant="light" color="gray" size="sm">
            closes {data.closesOn}
          </Badge>
        )}
        {data.status !== "OPEN" && (
          <Text size="xs" c="dimmed">
            Applications are only accepted while a vacancy is open.
          </Text>
        )}
      </Group>

      {error && (
        <Alert
          color="red"
          mb="md"
          withCloseButton
          onClose={() => setError(null)}
          icon={<IconAlertTriangle size={18} />}
        >
          {error}
        </Alert>
      )}

      <PipelineBar
        counts={pipeline.data ?? {}}
        activeStage={stageFilter}
        onSelect={setStageFilter}
      />

      <Title order={5} mt="lg" mb="sm">
        {stageFilter ? `${stageFilter.toLowerCase()} candidates` : "All candidates"}
      </Title>

      <Card withBorder radius="md" p={0}>
        <Table.ScrollContainer minWidth={820}>
          <Table striped highlightOnHover verticalSpacing="sm">
            <Table.Thead>
              <Table.Tr>
                <Table.Th>Candidate</Table.Th>
                <Table.Th>Applied</Table.Th>
                <Table.Th>Stage</Table.Th>
                <Table.Th>Score</Table.Th>
                <Table.Th>Next</Table.Th>
              </Table.Tr>
            </Table.Thead>
            <Table.Tbody>
              {items.map((application) => (
                <Table.Tr key={application.id}>
                  <Table.Td
                    style={{ cursor: "pointer" }}
                    onClick={() => setOpenApplication(application.id)}
                  >
                    <Text size="sm" fw={500}>
                      {application.applicant
                        ? localized(application.applicant.fullName, i18n.language)
                        : "—"}
                    </Text>
                    <Text size="xs" c="dimmed">
                      {application.applicant?.phoneNumber}
                      {application.applicant?.educationLevel &&
                        ` · ${application.applicant.educationLevel}`}
                    </Text>
                  </Table.Td>
                  <Table.Td>
                    <Text size="sm">{application.appliedOn}</Text>
                  </Table.Td>
                  <Table.Td>
                    <StageBadge stage={application.stage} />
                    {application.rejectionReason && (
                      <Text size="xs" c="dimmed" mt={2}>
                        {application.rejectionReason}
                      </Text>
                    )}
                  </Table.Td>
                  <Table.Td>
                    <Text size="sm">
                      {application.screeningScore
                        ? Number(application.screeningScore)
                        : "—"}
                    </Text>
                  </Table.Td>
                  <Table.Td>
                    {/* Only the moves the server would actually accept. */}
                    <Group gap={4}>
                      {(NEXT[application.stage] ?? []).map((stage) => (
                        <Can
                          key={stage}
                          permission={HR_PERMS.recruitment.screenApplication}
                        >
                          <Button
                            size="compact-xs"
                            variant={stage === "REJECTED" ? "subtle" : "light"}
                            color={stage === "REJECTED" ? "red" : undefined}
                            loading={advance.isPending}
                            onClick={() =>
                              stage === "REJECTED"
                                ? setRejecting(application.id)
                                : advance.mutate({ id: application.id, stage })
                            }
                          >
                            {stage.toLowerCase()}
                          </Button>
                        </Can>
                      ))}
                    </Group>
                  </Table.Td>
                </Table.Tr>
              ))}
              {!applications.isLoading && items.length === 0 && (
                <Table.Tr>
                  <Table.Td colSpan={5}>
                    <Text size="sm" c="dimmed" ta="center" py="lg">
                      No candidates {stageFilter ? "at this stage" : "yet"}.
                    </Text>
                  </Table.Td>
                </Table.Tr>
              )}
            </Table.Tbody>
          </Table>
        </Table.ScrollContainer>
      </Card>

      <AddApplicantModal
        opened={addingApplicant}
        onClose={() => setAddingApplicant(false)}
        jobOpeningId={id!}
        onDone={invalidate}
      />

      <ApplicationDrawer
        applicationId={openApplication}
        onClose={() => setOpenApplication(null)}
        openingPositionId={data.positionId ?? null}
      />

      <Modal
        opened={Boolean(rejecting)}
        onClose={() => setRejecting(null)}
        title="Reject this candidate"
        centered
      >
        <Stack>
          <Text size="sm">
            The reason is required. It is the record of why, and candidates ask.
          </Text>
          <Textarea
            autosize
            minRows={2}
            value={rejectReason}
            onChange={(event) => setRejectReason(event.currentTarget.value)}
          />
          <Group justify="flex-end">
            <Button variant="default" onClick={() => setRejecting(null)}>
              Cancel
            </Button>
            <Button
              color="red"
              disabled={!rejectReason.trim()}
              loading={advance.isPending}
              onClick={() =>
                rejecting &&
                advance.mutate({
                  id: rejecting,
                  stage: "REJECTED",
                  reason: rejectReason,
                })
              }
            >
              Reject
            </Button>
          </Group>
        </Stack>
      </Modal>
    </>
  );
}

function AddApplicantModal({
  opened,
  onClose,
  jobOpeningId,
  onDone,
}: {
  opened: boolean;
  onClose: () => void;
  jobOpeningId: string;
  onDone: () => void;
}) {
  const [fullName, setFullName] = useState({ am: "", en: "" });
  const [phoneNumber, setPhoneNumber] = useState("");
  const [email, setEmail] = useState("");
  const [educationLevel, setEducationLevel] = useState("");
  const [yearsExperience, setYearsExperience] = useState<number | "">(0);
  const [source, setSource] = useState("DIRECT");

  const submit = useMutation({
    mutationFn: async () => {
      // Two calls on purpose: the applicant may already exist from an earlier
      // vacancy, and the API matches on phone number rather than duplicating.
      const applicant = await upsertApplicant({
        fullName,
        phoneNumber,
        email: email || undefined,
        educationLevel: educationLevel || undefined,
        yearsExperience: yearsExperience ? String(yearsExperience) : undefined,
        source,
      });
      return apply({ jobOpeningId, applicantId: applicant.id });
    },
    onSuccess: () => {
      onDone();
      setFullName({ am: "", en: "" });
      setPhoneNumber("");
      setEmail("");
      onClose();
    },
  });

  return (
    <Modal opened={opened} onClose={onClose} title="Add a candidate" centered size="lg">
      <Stack>
        {submit.isError && (
          <Alert color="red" icon={<IconAlertTriangle size={18} />}>
            {apiErrorMessage(submit.error)}
          </Alert>
        )}
        <Alert color="blue" variant="light">
          No login is created. An applicant becomes an IAM account only if they
          are hired.
        </Alert>
        <BilingualTextInput
          label="Full name"
          required
          value={fullName}
          onChange={setFullName}
        />
        <Group grow>
          <TextInput
            label="Phone"
            required
            placeholder="+251911000000"
            description="How applicants are matched — a returning candidate is not duplicated."
            value={phoneNumber}
            onChange={(event) => setPhoneNumber(event.currentTarget.value)}
          />
          <TextInput
            label="Email"
            type="email"
            value={email}
            onChange={(event) => setEmail(event.currentTarget.value)}
          />
        </Group>
        <Group grow>
          <TextInput
            label="Education"
            placeholder="BSc"
            value={educationLevel}
            onChange={(event) => setEducationLevel(event.currentTarget.value)}
          />
          <NumberInput
            label="Years of experience"
            min={0}
            max={60}
            value={yearsExperience}
            onChange={(value) => setYearsExperience(value === "" ? "" : Number(value))}
          />
          <Select
            label="Source"
            allowDeselect={false}
            value={source}
            onChange={(value) => setSource(value ?? "DIRECT")}
            data={[
              "DIRECT",
              "REFERRAL",
              "AGENCY",
              "WEBSITE",
              "NEWSPAPER",
              "INTERNAL",
              "OTHER",
            ].map((value) => ({ value, label: value.toLowerCase() }))}
          />
        </Group>
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button
            loading={submit.isPending}
            disabled={!fullName.en || !phoneNumber}
            onClick={() => submit.mutate()}
          >
            Add and apply
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}
