import {
  Alert,
  Badge,
  Button,
  Card,
  Divider,
  Drawer,
  Group,
  Modal,
  NumberInput,
  Select,
  Stack,
  Text,
  TextInput,
  Textarea,
  Timeline,
} from "@mantine/core";
import {
  IconAlertTriangle,
  IconCalendarEvent,
  IconFileCheck,
  IconUserCheck,
} from "@tabler/icons-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { useTranslation } from "react-i18next";

import { Can } from "@/auth/Can";
import { HR_PERMS } from "@/auth/permissions";
import { apiErrorMessage } from "@/auth/http";
import { EthiopianDateInput } from "@/shared/components/EthiopianDateInput";
import { localized } from "@/shared/lib/localizedName";
import {
  createOffer,
  getApplication,
  hireFromOffer,
  listOffers,
  recordInterviewOutcome,
  respondToOffer,
  scheduleInterview,
  sendOffer,
} from "../api";
import { OfferBadge, StageBadge } from "./StageBadge";

/**
 * Everything about one candidate: their details, their interviews, and the
 * offer — including turning an accepted one into an actual employee.
 *
 * A drawer rather than a page because recruiters work down a list; sending them
 * to a separate route and back for each candidate loses their place.
 */
export function ApplicationDrawer({
  applicationId,
  onClose,
  openingPositionId,
}: {
  applicationId: string | null;
  onClose: () => void;
  openingPositionId: string | null;
}) {
  const queryClient = useQueryClient();
  const { i18n } = useTranslation();
  const [scheduling, setScheduling] = useState(false);
  const [offering, setOffering] = useState(false);
  const [hiring, setHiring] = useState(false);
  const [outcomeFor, setOutcomeFor] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  const application = useQuery({
    queryKey: ["application", applicationId],
    queryFn: () => getApplication(applicationId!),
    enabled: Boolean(applicationId),
  });

  const offers = useQuery({
    queryKey: ["offers", applicationId],
    queryFn: () => listOffers(applicationId!),
    enabled: Boolean(applicationId),
  });

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

  const act = useMutation({
    mutationFn: async (payload: { kind: string; id: string; accepted?: boolean; reason?: string }) => {
      if (payload.kind === "send") return sendOffer(payload.id);
      return respondToOffer(payload.id, payload.accepted!, payload.reason);
    },
    onSuccess: invalidate,
    onError: (err) => setError(apiErrorMessage(err)),
  });

  const data = application.data;
  const liveOffer = (offers.data ?? []).find(
    (offer) => offer.status === "DRAFT" || offer.status === "SENT",
  );
  const acceptedOffer = (offers.data ?? []).find(
    (offer) => offer.status === "ACCEPTED",
  );

  return (
    <Drawer
      opened={Boolean(applicationId)}
      onClose={onClose}
      position="right"
      size="xl"
      title={
        data?.applicant
          ? localized(data.applicant.fullName, i18n.language)
          : "Candidate"
      }
    >
      {!data ? null : (
        <Stack>
          {error && (
            <Alert
              color="red"
              withCloseButton
              onClose={() => setError(null)}
              icon={<IconAlertTriangle size={18} />}
            >
              {error}
            </Alert>
          )}

          <Group gap="xs">
            <StageBadge stage={data.stage} />
            <Text size="xs" c="dimmed">
              applied {data.appliedOn}
            </Text>
          </Group>

          <Card withBorder radius="md" padding="sm">
            <Group gap="xl">
              <Detail label="phone" value={data.applicant?.phoneNumber} />
              <Detail label="email" value={data.applicant?.email} />
              <Detail label="education" value={data.applicant?.educationLevel} />
              <Detail
                label="experience"
                value={
                  data.applicant?.yearsExperience
                    ? `${Number(data.applicant.yearsExperience)} years`
                    : undefined
                }
              />
              <Detail label="source" value={data.applicant?.source?.toLowerCase()} />
            </Group>
          </Card>

          <Divider label="Interviews" labelPosition="left" />

          {(data.interviews ?? []).length === 0 ? (
            <Text size="sm" c="dimmed">
              None scheduled.
            </Text>
          ) : (
            <Timeline bulletSize={20} lineWidth={2}>
              {(data.interviews ?? []).map((interview) => (
                <Timeline.Item
                  key={interview.id}
                  bullet={<IconCalendarEvent size={12} />}
                  title={`Round ${interview.round} — ${interview.interviewType.toLowerCase()}`}
                >
                  <Text size="xs" c="dimmed">
                    {new Date(interview.scheduledAt).toLocaleString()}
                    {interview.location && ` · ${interview.location}`}
                  </Text>
                  <Group gap="xs" mt={4}>
                    <Badge
                      size="xs"
                      variant="light"
                      color={
                        interview.status === "COMPLETED"
                          ? interview.recommendation === "ADVANCE"
                            ? "green"
                            : interview.recommendation === "REJECT"
                              ? "red"
                              : "yellow"
                          : "gray"
                      }
                    >
                      {interview.status === "COMPLETED"
                        ? `${interview.recommendation?.toLowerCase()}${
                            interview.score ? ` · ${Number(interview.score)}` : ""
                          }`
                        : interview.status.toLowerCase()}
                    </Badge>
                    {interview.status === "SCHEDULED" && (
                      <Can permission={HR_PERMS.recruitment.scheduleInterview}>
                        <Button
                          size="compact-xs"
                          variant="subtle"
                          onClick={() => setOutcomeFor(interview.id)}
                        >
                          Record outcome
                        </Button>
                      </Can>
                    )}
                  </Group>
                  {interview.feedback && (
                    <Text size="xs" mt={4}>
                      “{interview.feedback}”
                    </Text>
                  )}
                </Timeline.Item>
              ))}
            </Timeline>
          )}

          {(data.stage === "SHORTLISTED" || data.stage === "INTERVIEW") && (
            <Can permission={HR_PERMS.recruitment.scheduleInterview}>
              <Button
                variant="default"
                leftSection={<IconCalendarEvent size={16} />}
                onClick={() => setScheduling(true)}
              >
                Schedule an interview
              </Button>
            </Can>
          )}

          <Divider label="Offer" labelPosition="left" />

          {(offers.data ?? []).length === 0 && (
            <Text size="sm" c="dimmed">
              No offer made.
            </Text>
          )}

          {(offers.data ?? []).map((offer) => (
            <Card key={offer.id} withBorder radius="md" padding="sm">
              <Group justify="space-between" align="flex-start">
                <Stack gap={2}>
                  <Group gap="xs">
                    <Text size="sm" fw={600}>
                      {Number(offer.offeredBasicSalary).toLocaleString()} basic
                    </Text>
                    <OfferBadge status={offer.status} />
                  </Group>
                  <Text size="xs" c="dimmed">
                    starts {offer.proposedStartDate}
                    {offer.probationEndDate &&
                      ` · probation to ${offer.probationEndDate}`}
                    {offer.expiresOn && ` · expires ${offer.expiresOn}`}
                  </Text>
                  {offer.declineReason && (
                    <Text size="xs" c="red">
                      declined: {offer.declineReason}
                    </Text>
                  )}
                  {offer.hiredEmployeeId && (
                    <Text size="xs" c="green">
                      hired — employee {offer.hiredEmployeeId.slice(0, 8)}
                    </Text>
                  )}
                </Stack>
                <Group gap="xs">
                  {offer.status === "DRAFT" && (
                    <Can permission={HR_PERMS.recruitment.makeOffer}>
                      <Button
                        size="compact-sm"
                        loading={act.isPending}
                        onClick={() => act.mutate({ kind: "send", id: offer.id })}
                      >
                        Send
                      </Button>
                    </Can>
                  )}
                  {offer.status === "SENT" && (
                    <Can permission={HR_PERMS.recruitment.makeOffer}>
                      <Button
                        size="compact-sm"
                        color="green"
                        loading={act.isPending}
                        onClick={() =>
                          act.mutate({ kind: "respond", id: offer.id, accepted: true })
                        }
                      >
                        Accepted
                      </Button>
                      <Button
                        size="compact-sm"
                        variant="subtle"
                        color="red"
                        onClick={() => {
                          const reason = window.prompt(
                            "Why did they decline? (required)",
                          );
                          if (reason?.trim()) {
                            act.mutate({
                              kind: "respond",
                              id: offer.id,
                              accepted: false,
                              reason,
                            });
                          }
                        }}
                      >
                        Declined
                      </Button>
                    </Can>
                  )}
                  {offer.status === "ACCEPTED" && !offer.hiredEmployeeId && (
                    <Can permission={HR_PERMS.recruitment.hire}>
                      <Button
                        size="compact-sm"
                        leftSection={<IconUserCheck size={14} />}
                        onClick={() => setHiring(true)}
                      >
                        Hire
                      </Button>
                    </Can>
                  )}
                </Group>
              </Group>
            </Card>
          ))}

          {!liveOffer &&
            !acceptedOffer &&
            (data.stage === "SHORTLISTED" || data.stage === "INTERVIEW") && (
              <Can permission={HR_PERMS.recruitment.makeOffer}>
                <Button
                  leftSection={<IconFileCheck size={16} />}
                  onClick={() => setOffering(true)}
                >
                  Make an offer
                </Button>
              </Can>
            )}
        </Stack>
      )}

      <ScheduleModal
        opened={scheduling}
        onClose={() => setScheduling(false)}
        applicationId={applicationId}
        onDone={invalidate}
      />
      <OutcomeModal
        interviewId={outcomeFor}
        onClose={() => setOutcomeFor(null)}
        onDone={invalidate}
      />
      <OfferModal
        opened={offering}
        onClose={() => setOffering(false)}
        applicationId={applicationId}
        onDone={invalidate}
      />
      <HireModal
        opened={hiring}
        onClose={() => setHiring(false)}
        offerId={acceptedOffer?.id ?? null}
        defaultEmail={data?.applicant?.email ?? ""}
        openingPositionId={openingPositionId}
        onDone={invalidate}
      />
    </Drawer>
  );
}

function Detail({ label, value }: { label: string; value?: string | null }) {
  return (
    <div>
      <Text size="xs" c="dimmed">
        {label}
      </Text>
      <Text size="sm">{value || "—"}</Text>
    </div>
  );
}

function ScheduleModal({
  opened,
  onClose,
  applicationId,
  onDone,
}: {
  opened: boolean;
  onClose: () => void;
  applicationId: string | null;
  onDone: () => void;
}) {
  const [date, setDate] = useState<string | undefined>();
  const [time, setTime] = useState("09:00");
  const [interviewType, setInterviewType] = useState("PANEL");
  const [location, setLocation] = useState("");

  const submit = useMutation({
    mutationFn: () =>
      scheduleInterview({
        applicationId: applicationId!,
        scheduledAt: new Date(`${date}T${time}:00`).toISOString(),
        interviewType,
        location: location || undefined,
      }),
    onSuccess: () => {
      onDone();
      onClose();
    },
  });

  return (
    <Modal opened={opened} onClose={onClose} title="Schedule an interview" centered>
      <Stack>
        {submit.isError && (
          <Alert color="red" icon={<IconAlertTriangle size={18} />}>
            {apiErrorMessage(submit.error)}
          </Alert>
        )}
        <EthiopianDateInput label="Date" required value={date} onChange={setDate} />
        <Group grow>
          <TextInput
            label="Time"
            required
            type="time"
            value={time}
            onChange={(event) => setTime(event.currentTarget.value)}
          />
          <Select
            label="Type"
            allowDeselect={false}
            value={interviewType}
            onChange={(value) => setInterviewType(value ?? "PANEL")}
            data={["PHONE", "PANEL", "TECHNICAL", "WRITTEN", "PRACTICAL", "FINAL"].map(
              (value) => ({ value, label: value.toLowerCase() }),
            )}
          />
        </Group>
        <TextInput
          label="Where"
          value={location}
          onChange={(event) => setLocation(event.currentTarget.value)}
        />
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button loading={submit.isPending} disabled={!date} onClick={() => submit.mutate()}>
            Schedule
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}

function OutcomeModal({
  interviewId,
  onClose,
  onDone,
}: {
  interviewId: string | null;
  onClose: () => void;
  onDone: () => void;
}) {
  const [score, setScore] = useState<number | "">(70);
  const [recommendation, setRecommendation] = useState("ADVANCE");
  const [feedback, setFeedback] = useState("");

  const submit = useMutation({
    mutationFn: () =>
      recordInterviewOutcome(interviewId!, {
        score: score === "" ? undefined : Number(score),
        recommendation,
        feedback: feedback || undefined,
      }),
    onSuccess: () => {
      onDone();
      setFeedback("");
      onClose();
    },
  });

  return (
    <Modal
      opened={Boolean(interviewId)}
      onClose={onClose}
      title="How did it go?"
      centered
    >
      <Stack>
        {submit.isError && (
          <Alert color="red" icon={<IconAlertTriangle size={18} />}>
            {apiErrorMessage(submit.error)}
          </Alert>
        )}
        <Select
          label="Recommendation"
          required
          allowDeselect={false}
          value={recommendation}
          onChange={(value) => setRecommendation(value ?? "ADVANCE")}
          data={[
            { value: "ADVANCE", label: "Advance" },
            { value: "HOLD", label: "Hold" },
            { value: "REJECT", label: "Reject" },
          ]}
          description="Required — a completed interview with no verdict helps nobody."
        />
        <NumberInput
          label="Score"
          min={0}
          max={100}
          value={score}
          onChange={(value) => setScore(value === "" ? "" : Number(value))}
        />
        <Textarea
          label="Feedback"
          autosize
          minRows={3}
          value={feedback}
          onChange={(event) => setFeedback(event.currentTarget.value)}
        />
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button loading={submit.isPending} onClick={() => submit.mutate()}>
            Record
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}

function OfferModal({
  opened,
  onClose,
  applicationId,
  onDone,
}: {
  opened: boolean;
  onClose: () => void;
  applicationId: string | null;
  onDone: () => void;
}) {
  const [salary, setSalary] = useState("");
  const [startDate, setStartDate] = useState<string | undefined>();
  const [probationEnd, setProbationEnd] = useState<string | undefined>();
  const [expiresOn, setExpiresOn] = useState<string | undefined>();

  const submit = useMutation({
    mutationFn: () =>
      createOffer({
        applicationId: applicationId!,
        offeredBasicSalary: salary,
        proposedStartDate: startDate!,
        probationEndDate: probationEnd,
        expiresOn,
      }),
    onSuccess: () => {
      onDone();
      setSalary("");
      onClose();
    },
  });

  return (
    <Modal opened={opened} onClose={onClose} title="Make an offer" centered>
      <Stack>
        {submit.isError && (
          <Alert color="red" icon={<IconAlertTriangle size={18} />}>
            {apiErrorMessage(submit.error)}
          </Alert>
        )}
        <Alert color="blue" variant="light">
          This figure becomes their basic salary if they accept and are hired —
          payroll reads it directly.
        </Alert>
        <TextInput
          label="Basic salary"
          required
          placeholder="12000.00"
          value={salary}
          onChange={(event) => setSalary(event.currentTarget.value)}
        />
        <EthiopianDateInput
          label="Proposed start"
          required
          value={startDate}
          onChange={setStartDate}
        />
        <EthiopianDateInput
          label="Probation ends"
          description="Setting this starts them on PROBATION rather than ACTIVE."
          value={probationEnd}
          onChange={setProbationEnd}
        />
        <EthiopianDateInput
          label="Offer expires"
          value={expiresOn}
          onChange={setExpiresOn}
        />
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button
            loading={submit.isPending}
            disabled={!salary || !startDate}
            onClick={() => submit.mutate()}
          >
            Draft offer
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}

function HireModal({
  opened,
  onClose,
  offerId,
  defaultEmail,
  openingPositionId,
  onDone,
}: {
  opened: boolean;
  onClose: () => void;
  offerId: string | null;
  defaultEmail: string;
  openingPositionId: string | null;
  onDone: () => void;
}) {
  const [username, setUsername] = useState("");
  const [email, setEmail] = useState(defaultEmail);
  const [positionId, setPositionId] = useState(openingPositionId ?? "");
  const [employeeNumber, setEmployeeNumber] = useState("");

  const submit = useMutation({
    mutationFn: () =>
      hireFromOffer(offerId!, {
        username,
        email,
        positionId: positionId || undefined,
        employeeNumber: employeeNumber || undefined,
      }),
    onSuccess: () => {
      onDone();
      onClose();
    },
  });

  return (
    <Modal opened={opened} onClose={onClose} title="Hire this candidate" centered>
      <Stack>
        {submit.isError && (
          <Alert color="red" icon={<IconAlertTriangle size={18} />}>
            {apiErrorMessage(submit.error)}
          </Alert>
        )}
        <Alert color="blue" variant="light">
          This creates their IAM account, employee record, position assignment,
          HR profile and salary — the same flow used to hire anyone else.
        </Alert>
        <TextInput
          label="Username"
          required
          value={username}
          onChange={(event) => setUsername(event.currentTarget.value)}
        />
        <TextInput
          label="Email"
          required
          type="email"
          value={email}
          onChange={(event) => setEmail(event.currentTarget.value)}
        />
        <TextInput
          label="IAM position id"
          required={!openingPositionId}
          description={
            openingPositionId
              ? "Taken from the vacancy. Override only if they are joining a different post."
              : "The vacancy has no position recorded, so one is needed here."
          }
          value={positionId}
          onChange={(event) => setPositionId(event.currentTarget.value)}
        />
        <TextInput
          label="Employee number"
          description="Generated if left blank."
          value={employeeNumber}
          onChange={(event) => setEmployeeNumber(event.currentTarget.value)}
        />
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button
            loading={submit.isPending}
            disabled={!username || !email || !positionId}
            onClick={() => submit.mutate()}
          >
            Hire
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}
