"use client";

import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/Button";
import { Card, Field, Label } from "@/components/ui/Card";
import { Input } from "@/components/ui/Input";
import { Select } from "@/components/ui/Select";
import { Toggle } from "@/components/ui/Toggle";
import {
  createElection,
  publishElection,
  updateElection,
} from "@/lib/actions/elections";
import { EligibilityModelPicker } from "@/components/features/elections/EligibilityModelPicker";
import {
  BALLOT_SECRECY_LABELS,
  ELIGIBILITY_MODEL_LABELS,
  POSITION_TYPE_LABELS,
  RESULTS_VISIBILITY_LABELS,
  VERIFICATION_METHOD_LABELS,
  VOTING_METHOD_LABELS,
} from "@/config/elections";
import { cn } from "@/lib/utils/cn";
import type {
  BallotSecrecy,
  BallotVerificationMethod,
  EligibilityModel,
  ElectionPositionType,
  ElectionStatus,
  ResultsVisibility,
  VotingMethod,
} from "@/types";

const STEPS = [
  "Basics",
  "Eligibility model",
  "Positions & candidates",
  "Ballot & security",
  "Review & publish",
];

interface WizardCandidate {
  name: string;
  biography: string;
  goalsStatement: string;
}

interface WizardPosition {
  title: string;
  positionType: ElectionPositionType;
  seatsAvailable: number;
  candidates: WizardCandidate[];
}

export interface ElectionWizardInitialData {
  electionId: string;
  status: ElectionStatus;
  title: string;
  description: string | null;
  startAt: Date | string | null;
  endAt: Date | string | null;
  recordDate: string | null;
  isTestMode: boolean;
  openEnrollment: boolean;
  quorumRequired: boolean;
  quorumCount: number | null;
  votingMethod: VotingMethod;
  ballotSecrecy: BallotSecrecy;
  resultsVisibility: ResultsVisibility;
  digitalSignatureRequired: boolean;
  verificationMethod: BallotVerificationMethod;
  eligibilityModel: EligibilityModel;
  requiresAuthorizationRecord: boolean;
  ballotsCast: number;
  positions: WizardPosition[];
}

const emptyCandidate = (): WizardCandidate => ({
  name: "",
  biography: "",
  goalsStatement: "",
});

const emptyPosition = (): WizardPosition => ({
  title: "",
  positionType: "single_seat",
  seatsAvailable: 1,
  candidates: [emptyCandidate()],
});

function toDatetimeLocalValue(date: Date | string | null | undefined): string {
  if (!date) return "";
  const parsed = typeof date === "string" ? new Date(date) : date;
  if (Number.isNaN(parsed.getTime())) return "";
  const pad = (value: number) => String(value).padStart(2, "0");
  return `${parsed.getFullYear()}-${pad(parsed.getMonth() + 1)}-${pad(parsed.getDate())}T${pad(parsed.getHours())}:${pad(parsed.getMinutes())}`;
}

interface ElectionWizardProps {
  mode?: "create" | "edit";
  initial?: ElectionWizardInitialData;
}

export function ElectionWizard({
  mode = "create",
  initial,
}: ElectionWizardProps) {
  const router = useRouter();
  const isEdit = mode === "edit" && !!initial;
  const lockPositions = isEdit && (initial?.ballotsCast ?? 0) > 0;

  const [step, setStep] = useState(0);
  const [error, setError] = useState<string | null>(null);
  const [isPending, startTransition] = useTransition();

  const [title, setTitle] = useState(initial?.title ?? "");
  const [description, setDescription] = useState(initial?.description ?? "");
  const [startAt, setStartAt] = useState(() =>
    toDatetimeLocalValue(initial?.startAt)
  );
  const [endAt, setEndAt] = useState(() => toDatetimeLocalValue(initial?.endAt));
  const [recordDate, setRecordDate] = useState(initial?.recordDate ?? "");
  const [isTestMode, setIsTestMode] = useState(initial?.isTestMode ?? true);
  const [openEnrollment, setOpenEnrollment] = useState(
    initial?.openEnrollment ?? false
  );
  const [quorumRequired, setQuorumRequired] = useState(
    initial?.quorumRequired ?? false
  );
  const [quorumCount, setQuorumCount] = useState(
    String(initial?.quorumCount ?? 1)
  );

  const [positions, setPositions] = useState<WizardPosition[]>(
    initial?.positions?.length ? initial.positions : [emptyPosition()]
  );

  const [votingMethod, setVotingMethod] = useState<VotingMethod>(
    initial?.votingMethod ?? "plurality"
  );
  const [ballotSecrecy, setBallotSecrecy] = useState<BallotSecrecy>(
    initial?.ballotSecrecy ?? "open"
  );
  const [resultsVisibility, setResultsVisibility] =
    useState<ResultsVisibility>(initial?.resultsVisibility ?? "after_close");
  const [digitalSignatureRequired, setDigitalSignatureRequired] = useState(
    initial?.digitalSignatureRequired ?? false
  );
  const [verificationMethod, setVerificationMethod] =
    useState<BallotVerificationMethod>(initial?.verificationMethod ?? "email");
  const [eligibilityModel, setEligibilityModel] = useState<EligibilityModel>(
    initial?.eligibilityModel ?? "member"
  );
  const [requiresAuthorizationRecord, setRequiresAuthorizationRecord] =
    useState(initial?.requiresAuthorizationRecord ?? false);

  function updatePosition(index: number, patch: Partial<WizardPosition>) {
    if (lockPositions) return;
    setPositions((current) =>
      current.map((position, i) => (i === index ? { ...position, ...patch } : position))
    );
  }

  function updateCandidate(
    positionIndex: number,
    candidateIndex: number,
    patch: Partial<WizardCandidate>
  ) {
    if (lockPositions) return;
    setPositions((current) =>
      current.map((position, i) =>
        i === positionIndex
          ? {
              ...position,
              candidates: position.candidates.map((candidate, j) =>
                j === candidateIndex ? { ...candidate, ...patch } : candidate
              ),
            }
          : position
      )
    );
  }

  function validateStep(): string | null {
    if (step === 0) {
      if (!title.trim()) return "Title is required";
      if (quorumRequired && (!quorumCount || Number(quorumCount) < 1)) {
        return "Enter a quorum count";
      }
    }
    if (step === 2) {
      if (positions.length === 0) return "Add at least one position";
      for (const position of positions) {
        if (!position.title.trim()) return "Every position needs a title";
        if (position.candidates.length === 0) {
          return `Add a candidate for ${position.title || "each position"}`;
        }
        if (position.candidates.some((candidate) => !candidate.name.trim())) {
          return "Every candidate needs a name";
        }
      }
    }
    return null;
  }

  function goNext() {
    const message = validateStep();
    if (message) {
      setError(message);
      return;
    }
    setError(null);
    setStep((current) => Math.min(current + 1, STEPS.length - 1));
  }

  function payload() {
    return {
      title: title.trim(),
      description: description.trim() || null,
      startAt: startAt || null,
      endAt: endAt || null,
      recordDate: recordDate || null,
      isTestMode,
      openEnrollment: eligibilityModel === "member" ? openEnrollment : false,
      quorumRequired,
      quorumCount: quorumRequired ? Number(quorumCount) : null,
      votingMethod,
      ballotSecrecy,
      resultsVisibility,
      digitalSignatureRequired,
      verificationMethod,
      eligibilityModel,
      requiresAuthorizationRecord:
        eligibilityModel === "member" ? false : requiresAuthorizationRecord,
      positions: positions.map((position) => ({
        title: position.title.trim(),
        positionType: position.positionType,
        seatsAvailable:
          position.positionType === "multi_seat" ? position.seatsAvailable : 1,
        candidates: position.candidates.map((candidate) => ({
          name: candidate.name.trim(),
          biography: candidate.biography.trim() || null,
          goalsStatement: candidate.goalsStatement.trim() || null,
        })),
      })),
    };
  }

  function save(andPublish: boolean) {
    const message = validateStep();
    if (message) {
      setError(message);
      return;
    }
    setError(null);
    startTransition(async () => {
      if (isEdit && initial) {
        const { positions: nextPositions, ...rest } = payload();
        const updated = await updateElection({
          electionId: initial.electionId,
          ...rest,
          ...(lockPositions ? {} : { positions: nextPositions }),
        });
        if (!updated.success) {
          setError(updated.error);
          return;
        }
        if (andPublish && initial.status === "draft") {
          const published = await publishElection(initial.electionId);
          if (!published.success) {
            setError(published.error);
            return;
          }
        }
        router.push(`/app/elections/${initial.electionId}`);
        router.refresh();
        return;
      }

      const created = await createElection(payload());
      if (!created.success || !created.data) {
        setError(created.success ? "Could not create election" : created.error);
        return;
      }
      if (andPublish) {
        const published = await publishElection(created.data.electionId);
        if (!published.success) {
          router.push(`/app/elections/${created.data.electionId}`);
          setError(published.error);
          return;
        }
      }
      router.push(`/app/elections/${created.data.electionId}`);
    });
  }

  const reviewStepLabel = isEdit
    ? initial?.status === "draft"
      ? "Review & publish"
      : "Review & save"
    : "Review & publish";

  return (
    <Card className="p-5 sm:p-6">
      <div className="mb-6 flex flex-wrap items-center gap-3">
        {STEPS.map((label, index) => (
          <div key={label} className="flex items-center gap-2">
            <div
              className={cn(
                "flex h-7 w-7 items-center justify-center rounded-full text-xs font-bold",
                index === step
                  ? "bg-primary text-white"
                  : index < step
                    ? "bg-success text-white"
                    : "bg-border text-muted"
              )}
            >
              {index + 1}
            </div>
            <span
              className={cn(
                "text-[12.5px] font-semibold",
                index === step ? "text-text" : "text-muted"
              )}
            >
              {index === STEPS.length - 1 ? reviewStepLabel : label}
            </span>
            {index < STEPS.length - 1 && <div className="hidden h-px w-8 bg-border sm:block" />}
          </div>
        ))}
      </div>

      {step === 0 && (
        <div className="grid gap-4 md:grid-cols-2">
          <Field className="md:col-span-2">
            <Label htmlFor="election-title">Title</Label>
            <Input
              id="election-title"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              placeholder="2026 Board election"
            />
          </Field>
          <Field className="md:col-span-2">
            <Label htmlFor="election-description">Description</Label>
            <textarea
              id="election-description"
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              rows={4}
              className="w-full rounded-sm border border-border px-3 py-2.5 text-sm focus:border-primary focus:outline-none"
              placeholder="What this election is deciding"
            />
          </Field>
          <Field>
            <Label htmlFor="start-at">Start</Label>
            <Input
              id="start-at"
              type="datetime-local"
              value={startAt}
              onChange={(e) => setStartAt(e.target.value)}
            />
          </Field>
          <Field>
            <Label htmlFor="end-at">End</Label>
            <Input
              id="end-at"
              type="datetime-local"
              value={endAt}
              onChange={(e) => setEndAt(e.target.value)}
            />
          </Field>
          <Field>
            <Label htmlFor="record-date">Record date</Label>
            <Input
              id="record-date"
              type="date"
              value={recordDate}
              onChange={(e) => setRecordDate(e.target.value)}
            />
            <p className="mt-1 text-[11px] text-muted">
              Eligibility is snapshotted as of this date when you publish.
            </p>
          </Field>
          <Field>
            <Label htmlFor="quorum-count">Quorum</Label>
            <div className="flex items-center gap-3">
              <Toggle checked={quorumRequired} onChange={setQuorumRequired} />
              <Input
                id="quorum-count"
                type="number"
                min={1}
                disabled={!quorumRequired}
                value={quorumCount}
                onChange={(e) => setQuorumCount(e.target.value)}
                className="max-w-[120px]"
              />
            </div>
          </Field>
          <Field className="md:col-span-2">
            <div className="flex items-center justify-between rounded-lg border border-border bg-bg px-4 py-3">
              <div>
                <p className="text-[13px] font-semibold">Test mode</p>
                <p className="text-[12px] text-muted">
                  Hidden from live workspace dashboards and exports.
                </p>
              </div>
              <Toggle checked={isTestMode} onChange={setIsTestMode} />
            </div>
          </Field>
        </div>
      )}

      {step === 1 && (
        <div className="space-y-4">
          <EligibilityModelPicker
            value={eligibilityModel}
            onChange={(model) => {
              setEligibilityModel(model);
              if (model === "member") setRequiresAuthorizationRecord(false);
              else setOpenEnrollment(false);
            }}
          />
          {eligibilityModel === "member" && (
            <div className="space-y-2">
              <p className="text-[13px] font-semibold">Who can vote?</p>
              <div className="grid gap-3 sm:grid-cols-2">
                <button
                  type="button"
                  onClick={() => setOpenEnrollment(true)}
                  className={cn(
                    "rounded-lg border px-4 py-3 text-left",
                    openEnrollment
                      ? "border-primary bg-[#EFF6FF]"
                      : "border-border bg-white"
                  )}
                >
                  <p className="text-[13px] font-bold">Open vote</p>
                  <p className="mt-1 text-[12px] text-muted">
                    Anyone with the ballot link can vote after email verification.
                    One vote per email — no pre-built voter roll required.
                  </p>
                </button>
                <button
                  type="button"
                  onClick={() => setOpenEnrollment(false)}
                  className={cn(
                    "rounded-lg border px-4 py-3 text-left",
                    !openEnrollment
                      ? "border-primary bg-[#EFF6FF]"
                      : "border-border bg-white"
                  )}
                >
                  <p className="text-[13px] font-bold">Email / voter roll</p>
                  <p className="mt-1 text-[12px] text-muted">
                    Only emails on the Audience contacts voter roll can vote.
                    Add contacts, then regenerate the roll.
                  </p>
                </button>
              </div>
            </div>
          )}
          {eligibilityModel !== "member" && (
            <div className="flex items-center justify-between rounded-lg border border-border bg-bg px-4 py-3">
              <div>
                <p className="text-[13px] font-semibold">
                  Require authorization for multi-owner entities
                </p>
                <p className="text-[12px] text-muted">
                  Entities with more than one owner stay pending until a valid
                  authorization record exists as of the record date.
                </p>
              </div>
              <Toggle
                checked={requiresAuthorizationRecord}
                onChange={setRequiresAuthorizationRecord}
              />
            </div>
          )}
        </div>
      )}

      {step === 2 && (
        <div className="space-y-4">
          {lockPositions && (
            <p className="rounded-md bg-warning-bg px-3 py-2 text-[12px] text-warning">
              Positions and candidates are locked because ballots have already
              been cast. Other election settings can still be edited.
            </p>
          )}
          {positions.map((position, index) => (
            <div key={index} className="rounded-lg border border-border p-4">
              <div className="mb-3 flex items-center justify-between">
                <p className="text-[13px] font-bold">Position {index + 1}</p>
                {!lockPositions && positions.length > 1 && (
                  <button
                    type="button"
                    className="text-xs font-semibold text-danger"
                    onClick={() =>
                      setPositions((current) => current.filter((_, i) => i !== index))
                    }
                  >
                    Remove
                  </button>
                )}
              </div>
              <div className="grid gap-3 md:grid-cols-3">
                <Field className="md:col-span-2">
                  <Label>Title</Label>
                  <Input
                    value={position.title}
                    onChange={(e) => updatePosition(index, { title: e.target.value })}
                    placeholder="Board seat"
                    disabled={lockPositions}
                  />
                </Field>
                <Field>
                  <Label>Type</Label>
                  <Select
                    value={position.positionType}
                    onChange={(e) =>
                      updatePosition(index, {
                        positionType: e.target.value as ElectionPositionType,
                      })
                    }
                    disabled={lockPositions}
                  >
                    {Object.entries(POSITION_TYPE_LABELS).map(([value, label]) => (
                      <option key={value} value={value}>
                        {label}
                      </option>
                    ))}
                  </Select>
                </Field>
                {position.positionType === "multi_seat" && (
                  <Field>
                    <Label>Seats</Label>
                    <Input
                      type="number"
                      min={1}
                      value={position.seatsAvailable}
                      onChange={(e) =>
                        updatePosition(index, {
                          seatsAvailable: Number(e.target.value) || 1,
                        })
                      }
                      disabled={lockPositions}
                    />
                  </Field>
                )}
              </div>

              <div className="mt-2 space-y-3">
                {position.candidates.map((candidate, cIndex) => (
                  <div key={cIndex} className="rounded-md border border-border bg-bg p-3">
                    <div className="mb-2 flex items-center justify-between">
                      <p className="text-[12px] font-semibold text-muted">
                        Candidate {cIndex + 1}
                      </p>
                      {!lockPositions && position.candidates.length > 1 && (
                        <button
                          type="button"
                          className="text-xs font-semibold text-danger"
                          onClick={() =>
                            updatePosition(index, {
                              candidates: position.candidates.filter((_, i) => i !== cIndex),
                            })
                          }
                        >
                          Remove
                        </button>
                      )}
                    </div>
                    <Input
                      value={candidate.name}
                      onChange={(e) =>
                        updateCandidate(index, cIndex, { name: e.target.value })
                      }
                      placeholder="Full name"
                      className="mb-2"
                      disabled={lockPositions}
                    />
                    <textarea
                      value={candidate.biography}
                      onChange={(e) =>
                        updateCandidate(index, cIndex, { biography: e.target.value })
                      }
                      rows={2}
                      placeholder="Biography (optional)"
                      disabled={lockPositions}
                      className="mb-2 w-full rounded-sm border border-border px-3 py-2 text-sm focus:border-primary focus:outline-none disabled:opacity-60"
                    />
                    <textarea
                      value={candidate.goalsStatement}
                      onChange={(e) =>
                        updateCandidate(index, cIndex, { goalsStatement: e.target.value })
                      }
                      rows={2}
                      placeholder="Goals (optional)"
                      disabled={lockPositions}
                      className="w-full rounded-sm border border-border px-3 py-2 text-sm focus:border-primary focus:outline-none disabled:opacity-60"
                    />
                  </div>
                ))}
                {!lockPositions && (
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={() =>
                      updatePosition(index, {
                        candidates: [...position.candidates, emptyCandidate()],
                      })
                    }
                  >
                    Add candidate
                  </Button>
                )}
              </div>
            </div>
          ))}
          {!lockPositions && (
            <Button
              type="button"
              variant="outline"
              onClick={() => setPositions((current) => [...current, emptyPosition()])}
            >
              Add position
            </Button>
          )}
        </div>
      )}

      {step === 3 && (
        <div className="grid gap-4 md:grid-cols-2">
          <Field>
            <Label>Voting method</Label>
            <Select
              value={votingMethod}
              onChange={(e) => setVotingMethod(e.target.value as VotingMethod)}
            >
              {Object.entries(VOTING_METHOD_LABELS).map(([value, label]) => (
                <option key={value} value={value}>
                  {label}
                </option>
              ))}
            </Select>
          </Field>
          <Field>
            <Label>Results visibility</Label>
            <Select
              value={resultsVisibility}
              onChange={(e) =>
                setResultsVisibility(e.target.value as ResultsVisibility)
              }
            >
              {Object.entries(RESULTS_VISIBILITY_LABELS).map(([value, label]) => (
                <option key={value} value={value}>
                  {label}
                </option>
              ))}
            </Select>
          </Field>
          <Field className="md:col-span-2">
            <Label>Ballot secrecy</Label>
            <div className="grid gap-3 sm:grid-cols-2">
              {(["open", "secret"] as const).map((value) => (
                <button
                  key={value}
                  type="button"
                  onClick={() => setBallotSecrecy(value)}
                  className={cn(
                    "rounded-lg border px-4 py-3 text-left",
                    ballotSecrecy === value
                      ? "border-primary bg-[#EFF6FF]"
                      : "border-border bg-white"
                  )}
                >
                  <p className="text-[13px] font-bold">{BALLOT_SECRECY_LABELS[value]}</p>
                  <p className="mt-1 text-[12px] text-muted">
                    {value === "secret"
                      ? "The stored ballot cannot be joined back to a voter."
                      : "Ballots retain a voter-roll link for audit."}
                  </p>
                </button>
              ))}
            </div>
          </Field>
          <Field className="md:col-span-2">
            <Label>Voter verification</Label>
            <Select
              value={verificationMethod}
              onChange={(e) =>
                setVerificationMethod(e.target.value as BallotVerificationMethod)
              }
            >
              {Object.entries(VERIFICATION_METHOD_LABELS).map(([value, label]) => (
                <option key={value} value={value}>
                  {label}
                </option>
              ))}
            </Select>
            <p className="mt-1 text-[11px] text-muted">
              Voters must enter a one-time code before the ballot opens.
            </p>
          </Field>
          <Field className="md:col-span-2">
            <div className="flex items-center justify-between rounded-lg border border-border bg-bg px-4 py-3">
              <div>
                <p className="text-[13px] font-semibold">Require consent on the ballot</p>
                <p className="text-[12px] text-muted">
                  Voters must confirm before the ballot is recorded.
                </p>
              </div>
              <Toggle
                checked={digitalSignatureRequired}
                onChange={setDigitalSignatureRequired}
              />
            </div>
          </Field>
        </div>
      )}

      {step === 4 && (
        <div className="space-y-3 text-[13px]">
          <ReviewRow label="Title" value={title} />
          <ReviewRow label="Test mode" value={isTestMode ? "Yes" : "No"} />
          <ReviewRow
            label="Who can vote"
            value={
              eligibilityModel === "member" && openEnrollment
                ? "Open vote — anyone with the link"
                : "Email / voter roll only"
            }
          />
          <ReviewRow
            label="Eligibility"
            value={ELIGIBILITY_MODEL_LABELS[eligibilityModel]}
          />
          <ReviewRow
            label="Authorization gate"
            value={
              eligibilityModel !== "member" && requiresAuthorizationRecord
                ? "Required for multi-owner entities"
                : "Off"
            }
          />
          <ReviewRow label="Voting method" value={VOTING_METHOD_LABELS[votingMethod]} />
          <ReviewRow label="Ballot" value={BALLOT_SECRECY_LABELS[ballotSecrecy]} />
          <ReviewRow
            label="Verification"
            value={VERIFICATION_METHOD_LABELS[verificationMethod]}
          />
          <ReviewRow
            label="Results"
            value={RESULTS_VISIBILITY_LABELS[resultsVisibility]}
          />
          <ReviewRow
            label="Positions"
            value={positions.map((position) => position.title || "Untitled").join(", ")}
          />
          {!isEdit && (
            <p className="rounded-lg bg-bg px-4 py-3 text-[12px] text-muted">
              {eligibilityModel === "member"
                ? "Publishing snapshots workspace contacts onto the voter roll as of the record date."
                : "Publishing snapshots voting entities onto the voter roll as of the record date. Create entities on the Voter Roll tab before publishing if needed."}
            </p>
          )}
        </div>
      )}

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

      <div className="mt-6 flex flex-wrap items-center justify-between gap-2">
        <Button
          type="button"
          variant="outline"
          disabled={step === 0 || isPending}
          onClick={() => {
            setError(null);
            setStep((current) => Math.max(0, current - 1));
          }}
        >
          Back
        </Button>
        <div className="flex flex-wrap gap-2">
          {step === STEPS.length - 1 ? (
            isEdit ? (
              <>
                <Button
                  type="button"
                  variant="outline"
                  loading={isPending}
                  onClick={() => save(false)}
                >
                  Save changes
                </Button>
                {initial?.status === "draft" && (
                  <Button type="button" loading={isPending} onClick={() => save(true)}>
                    Publish election
                  </Button>
                )}
              </>
            ) : (
              <>
                <Button
                  type="button"
                  variant="outline"
                  loading={isPending}
                  onClick={() => save(false)}
                >
                  Save draft
                </Button>
                <Button type="button" loading={isPending} onClick={() => save(true)}>
                  Publish election
                </Button>
              </>
            )
          ) : (
            <Button type="button" onClick={goNext}>
              Continue
            </Button>
          )}
        </div>
      </div>
    </Card>
  );
}

function ReviewRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex items-start justify-between gap-4 border-b border-border py-2">
      <span className="text-muted">{label}</span>
      <span className="text-right font-semibold">{value || "—"}</span>
    </div>
  );
}
