"use client";

import { useMemo, useRef, useState, useTransition, type PointerEvent } from "react";
import { Castle, Check } from "lucide-react";
import { Button } from "@/components/ui/Button";
import { Input } from "@/components/ui/Input";
import {
  castBallot,
  confirmVoterVerification,
  requestVoterVerification,
} from "@/lib/actions/ballots";
import { isRankBasedVotingMethod, VERIFICATION_METHOD_LABELS } from "@/config/elections";
import { cn } from "@/lib/utils/cn";
import type { PublicElectionData, PublicPosition } from "@/lib/queries/election-filler-queries";
import type { DigitalSignatureType } from "@/types";

type Phase = "welcome" | "verify" | "otp" | "ballot" | "sign" | "receipt";

interface ElectionBallotProps {
  data: PublicElectionData;
  embed?: boolean;
}

interface SelectionState {
  candidateIds: string[];
  ranks: Record<string, number>;
}

export function ElectionBallot({ data, embed = false }: ElectionBallotProps) {
  const rankBased = isRankBasedVotingMethod(data.votingMethod);
  const needsPhone =
    data.verificationMethod === "sms_2fa" ||
    data.verificationMethod === "email_sms";

  const [phase, setPhase] = useState<Phase>("welcome");
  const [email, setEmail] = useState("");
  const [phone, setPhone] = useState("");
  const [otpCode, setOtpCode] = useState("");
  const [challengeToken, setChallengeToken] = useState("");
  const [maskedDestination, setMaskedDestination] = useState("");
  const [debugCode, setDebugCode] = useState<string | null>(null);
  const [voterName, setVoterName] = useState("");
  const [votingToken, setVotingToken] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [receiptCode, setReceiptCode] = useState<string | null>(null);
  const [consentAccepted, setConsentAccepted] = useState(false);
  const [signatureType, setSignatureType] =
    useState<DigitalSignatureType>("checkbox_consent");
  const [typedName, setTypedName] = useState("");
  const [drawnPath, setDrawnPath] = useState("");
  const [isPending, startTransition] = useTransition();
  const [selections, setSelections] = useState<Record<string, SelectionState>>({});
  const drawingRef = useRef<HTMLCanvasElement | null>(null);
  const drawing = useRef(false);

  const canVote = data.votingOpen;

  function toggleCandidate(position: PublicPosition, candidateId: string) {
    setSelections((current) => {
      const existing = current[position.id] ?? { candidateIds: [], ranks: {} };
      const selected = existing.candidateIds.includes(candidateId);

      if (rankBased) {
        const nextIds = selected
          ? existing.candidateIds.filter((id) => id !== candidateId)
          : [...existing.candidateIds, candidateId];
        const ranks: Record<string, number> = {};
        nextIds.forEach((id, index) => {
          ranks[id] = index + 1;
        });
        return { ...current, [position.id]: { candidateIds: nextIds, ranks } };
      }

      if (data.votingMethod === "approval" || position.positionType === "multi_seat") {
        const max =
          data.votingMethod === "approval"
            ? position.candidates.length
            : position.seatsAvailable;
        let nextIds = selected
          ? existing.candidateIds.filter((id) => id !== candidateId)
          : [...existing.candidateIds, candidateId];
        if (nextIds.length > max) nextIds = nextIds.slice(-max);
        return { ...current, [position.id]: { candidateIds: nextIds, ranks: {} } };
      }

      return {
        ...current,
        [position.id]: { candidateIds: [candidateId], ranks: {} },
      };
    });
  }

  const ballotReady = useMemo(() => {
    return data.positions.every((position) => {
      const selected = selections[position.id]?.candidateIds.length ?? 0;
      return selected > 0;
    });
  }, [data.positions, selections]);

  function handleRequestCode() {
    setError(null);
    startTransition(async () => {
      const result = await requestVoterVerification({
        slug: data.slug,
        email,
        phone: needsPhone ? phone : null,
      });
      if (!result.success || !result.data) {
        setError(result.success ? "Could not send code" : result.error);
        return;
      }
      setChallengeToken(result.data.challengeToken);
      setMaskedDestination(result.data.maskedDestination);
      setDebugCode(result.data.debugCode ?? null);
      setPhase("otp");
    });
  }

  function handleConfirmCode() {
    setError(null);
    startTransition(async () => {
      const result = await confirmVoterVerification({
        challengeToken,
        code: otpCode,
      });
      if (!result.success || !result.data) {
        setError(result.success ? "Could not verify code" : result.error);
        return;
      }
      setVotingToken(result.data.votingToken);
      setVoterName(result.data.voterName);
      setTypedName(result.data.voterName);
      setPhase("ballot");
    });
  }

  function handleCast() {
    setError(null);
    if (data.digitalSignatureRequired) {
      setPhase("sign");
      return;
    }
    submitBallot();
  }

  function canvasPointer(event: PointerEvent<HTMLCanvasElement>, mode: "start" | "move" | "end") {
    const canvas = drawingRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    const rect = canvas.getBoundingClientRect();
    const x = event.clientX - rect.left;
    const y = event.clientY - rect.top;

    if (mode === "start") {
      drawing.current = true;
      ctx.beginPath();
      ctx.moveTo(x, y);
      return;
    }
    if (mode === "move" && drawing.current) {
      ctx.lineWidth = 2;
      ctx.lineCap = "round";
      ctx.strokeStyle = "#0B1730";
      ctx.lineTo(x, y);
      ctx.stroke();
      return;
    }
    if (mode === "end" && drawing.current) {
      drawing.current = false;
      setDrawnPath(canvas.toDataURL("image/png"));
    }
  }

  function submitBallot() {
    const payload = data.positions.flatMap((position) => {
      const state = selections[position.id];
      return (state?.candidateIds ?? []).map((candidateId) => ({
        electionPositionId: position.id,
        candidateId,
        rank: rankBased ? state?.ranks[candidateId] ?? null : null,
      }));
    });

    startTransition(async () => {
      const result = await castBallot({
        votingToken,
        selections: payload,
        consentAccepted: data.digitalSignatureRequired ? consentAccepted : true,
        signerName:
          signatureType === "typed"
            ? typedName
            : signatureType === "checkbox_consent"
              ? voterName
              : typedName || voterName,
        signatureType: data.digitalSignatureRequired ? signatureType : null,
        drawnSignaturePath:
          signatureType === "drawn" ? drawnPath || null : null,
      });
      if (!result.success || !result.data) {
        setError(result.success ? "Could not cast ballot" : result.error);
        return;
      }
      setReceiptCode(result.data.receiptCode);
      setPhase("receipt");
    });
  }

  const signReady =
    consentAccepted &&
    (signatureType === "checkbox_consent" ||
      (signatureType === "typed" && typedName.trim().length > 0) ||
      (signatureType === "drawn" && drawnPath.length > 0));

  return (
    <div
      className={cn(
        "mx-auto min-h-screen max-w-2xl px-4 py-10",
        embed && "min-h-0 max-w-none px-3 py-4"
      )}
    >
      {!embed && (
        <div className="mb-8 flex items-center gap-2 text-navy">
          <Castle className="h-5 w-5 text-accent" />
          <span className="text-sm font-extrabold tracking-wide">
            SurveyStronghold
          </span>
        </div>
      )}

      <div
        className={cn(
          "rounded-lg border border-border bg-card p-6 shadow",
          embed && "rounded-none border-0 shadow-none"
        )}
      >
        <p className="text-[11px] font-bold uppercase tracking-wide text-muted">Election</p>
        <h1 className="mt-1 text-2xl font-extrabold">{data.title}</h1>
        {data.description && (
          <p className="mt-2 text-sm text-muted">{data.description}</p>
        )}

        {phase === "welcome" && (
          <div className="mt-6">
            {!canVote ? (
              <p className="rounded-md bg-warning-bg px-3 py-2 text-sm text-warning">
                {data.closedReason ?? "Voting is not open."}
              </p>
            ) : (
              <>
                <p className="text-sm text-muted">
                  Verify with {VERIFICATION_METHOD_LABELS[data.verificationMethod].toLowerCase()},
                  then mark your ballot.
                  {data.ballotSecrecy === "secret"
                    ? " This is a secret ballot — your choices are stored without a voter link."
                    : ""}
                </p>
                <Button className="mt-5" onClick={() => setPhase("verify")}>
                  Continue
                </Button>
              </>
            )}
          </div>
        )}

        {phase === "verify" && (
          <div className="mt-6 space-y-3">
            <label className="text-[12.5px] font-semibold">
              {data.openEnrollment ? "Your email" : "Email on the voter roll"}
            </label>
            <Input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="you@example.com"
            />
            {data.openEnrollment && (
              <p className="text-[11px] text-muted">
                Anyone with this link can vote once per email after verification.
              </p>
            )}
            {needsPhone && (
              <>
                <label className="text-[12.5px] font-semibold">Mobile number</label>
                <Input
                  type="tel"
                  value={phone}
                  onChange={(e) => setPhone(e.target.value)}
                  placeholder="+1 555 0100"
                />
              </>
            )}
            <Button loading={isPending} onClick={handleRequestCode}>
              Send verification code
            </Button>
          </div>
        )}

        {phase === "otp" && (
          <div className="mt-6 space-y-3">
            <p className="text-sm text-muted">
              Enter the 6-digit code sent to {maskedDestination}.
            </p>
            {debugCode && (
              <p className="rounded-md bg-[#EFF6FF] px-3 py-2 text-[12px] text-primary">
                Dev delivery fallback code: <span className="font-mono font-bold">{debugCode}</span>
              </p>
            )}
            <Input
              inputMode="numeric"
              maxLength={6}
              value={otpCode}
              onChange={(e) => setOtpCode(e.target.value.replace(/\D/g, "").slice(0, 6))}
              placeholder="123456"
            />
            <div className="flex flex-wrap gap-2">
              <Button loading={isPending} onClick={handleConfirmCode}>
                Confirm code
              </Button>
              <Button
                type="button"
                variant="outline"
                loading={isPending}
                onClick={handleRequestCode}
              >
                Resend
              </Button>
            </div>
          </div>
        )}

        {phase === "ballot" && (
          <div className="mt-6 space-y-6">
            <p className="text-sm text-muted">
              Voting as <span className="font-semibold text-text">{voterName}</span>
            </p>
            {data.positions.map((position) => {
              const selected = selections[position.id]?.candidateIds ?? [];
              return (
                <div key={position.id}>
                  <h2 className="text-[15px] font-bold">{position.title}</h2>
                  <p className="mb-3 text-[12px] text-muted">
                    {rankBased
                      ? "Select candidates in preference order"
                      : data.votingMethod === "approval"
                        ? "Select every candidate you approve"
                        : position.positionType === "multi_seat"
                          ? `Select up to ${position.seatsAvailable}`
                          : "Select one"}
                  </p>
                  <div className="space-y-2">
                    {position.candidates.map((candidate) => {
                      const active = selected.includes(candidate.id);
                      const rank = selections[position.id]?.ranks[candidate.id];
                      return (
                        <button
                          key={candidate.id}
                          type="button"
                          onClick={() => toggleCandidate(position, candidate.id)}
                          className={cn(
                            "w-full rounded-lg border px-4 py-3 text-left",
                            active
                              ? "border-primary bg-[#EFF6FF]"
                              : "border-border bg-white hover:border-primary/40"
                          )}
                        >
                          <div className="flex items-start justify-between gap-3">
                            <div>
                              <p className="font-semibold">{candidate.name}</p>
                              {candidate.biography && (
                                <p className="mt-1 text-[13px] text-muted">
                                  {candidate.biography}
                                </p>
                              )}
                            </div>
                            {active && (
                              <span className="flex h-6 w-6 items-center justify-center rounded-full bg-primary text-white">
                                {rankBased && rank ? rank : <Check className="h-3.5 w-3.5" />}
                              </span>
                            )}
                          </div>
                        </button>
                      );
                    })}
                  </div>
                </div>
              );
            })}
            <Button disabled={!ballotReady} loading={isPending} onClick={handleCast}>
              {data.digitalSignatureRequired ? "Continue to signature" : "Cast ballot"}
            </Button>
          </div>
        )}

        {phase === "sign" && (
          <div className="mt-6 space-y-4">
            <p className="text-sm text-muted">
              Confirm that you are {voterName} and that this is your official ballot.
            </p>
            <div className="grid gap-2 sm:grid-cols-3">
              {(
                [
                  ["checkbox_consent", "Checkbox"],
                  ["typed", "Typed name"],
                  ["drawn", "Draw"],
                ] as const
              ).map(([value, label]) => (
                <button
                  key={value}
                  type="button"
                  onClick={() => setSignatureType(value)}
                  className={cn(
                    "rounded-lg border px-3 py-2 text-[12.5px] font-semibold",
                    signatureType === value
                      ? "border-primary bg-[#EFF6FF] text-primary"
                      : "border-border"
                  )}
                >
                  {label}
                </button>
              ))}
            </div>
            {signatureType === "typed" && (
              <Input
                value={typedName}
                onChange={(e) => setTypedName(e.target.value)}
                placeholder="Type your full legal name"
              />
            )}
            {signatureType === "drawn" && (
              <div>
                <canvas
                  ref={drawingRef}
                  width={480}
                  height={160}
                  className="w-full rounded-lg border border-border bg-white touch-none"
                  onPointerDown={(e) => canvasPointer(e, "start")}
                  onPointerMove={(e) => canvasPointer(e, "move")}
                  onPointerUp={(e) => canvasPointer(e, "end")}
                  onPointerLeave={(e) => canvasPointer(e, "end")}
                />
                <Button
                  type="button"
                  variant="outline"
                  size="sm"
                  className="mt-2"
                  onClick={() => {
                    const canvas = drawingRef.current;
                    const ctx = canvas?.getContext("2d");
                    if (canvas && ctx) {
                      ctx.clearRect(0, 0, canvas.width, canvas.height);
                      setDrawnPath("");
                    }
                  }}
                >
                  Clear signature
                </Button>
              </div>
            )}
            <label className="flex items-start gap-2 text-sm">
              <input
                type="checkbox"
                checked={consentAccepted}
                onChange={(e) => setConsentAccepted(e.target.checked)}
                className="mt-1"
              />
              I consent to submit this ballot and understand my vote will be recorded.
            </label>
            <Button
              disabled={!signReady}
              loading={isPending}
              onClick={submitBallot}
            >
              Sign and cast
            </Button>
          </div>
        )}

        {phase === "receipt" && receiptCode && (
          <div className="mt-6 text-center">
            <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-success-bg text-success">
              <Check className="h-6 w-6" />
            </div>
            <h2 className="text-lg font-bold">Ballot recorded</h2>
            <p className="mt-2 text-sm text-muted">Keep this receipt code for your records.</p>
            <p className="mt-4 font-mono text-xl font-extrabold tracking-[0.2em]">
              {receiptCode}
            </p>
          </div>
        )}

        {error && (
          <p className="mt-4 rounded-md bg-danger-bg px-3 py-2 text-[13px] text-danger">
            {error}
          </p>
        )}
      </div>
    </div>
  );
}
