import { Op } from "sequelize";
import { initDb } from "@/lib/db";
import type { Candidate } from "@/lib/db/models/candidate";
import type { CandidateQA } from "@/lib/db/models/candidate-qa";
import type { ElectionPosition } from "@/lib/db/models/election-position";
import type {
  BallotSecrecy,
  BallotVerificationMethod,
  ElectionPositionType,
  ResultsVisibility,
  VotingMethod,
} from "@/types";

export interface PublicCandidate {
  id: string;
  name: string;
  biography: string | null;
  goalsStatement: string | null;
  photoUrl: string | null;
  qas: { id: string; question: string; answer: string }[];
}

export interface PublicPosition {
  id: string;
  title: string;
  positionType: ElectionPositionType;
  seatsAvailable: number;
  candidates: PublicCandidate[];
}

export interface PublicElectionData {
  id: string;
  title: string;
  description: string | null;
  slug: string;
  status: "open" | "closed" | "certified";
  ballotSecrecy: BallotSecrecy;
  votingMethod: VotingMethod;
  digitalSignatureRequired: boolean;
  verificationMethod: BallotVerificationMethod;
  resultsVisibility: ResultsVisibility;
  startAt: Date | null;
  endAt: Date | null;
  votingOpen: boolean;
  closedReason: string | null;
  openEnrollment: boolean;
  positions: PublicPosition[];
}

export async function getPublicElectionBySlug(
  slug: string
): Promise<PublicElectionData | null> {
  const { Election, ElectionPosition, Candidate, CandidateQA } = initDb();

  const election = await Election.findOne({
    where: { slug, status: { [Op.in]: ["open", "closed", "certified"] } },
    include: [
      {
        model: ElectionPosition,
        as: "positions",
        include: [
          {
            model: Candidate,
            as: "candidates",
            where: { status: "approved" },
            required: false,
            include: [{ model: CandidateQA, as: "qas" }],
          },
        ],
      },
    ],
  });

  if (!election) return null;

  const now = Date.now();
  let votingOpen = election.status === "open";
  let closedReason: string | null = null;

  if (election.status !== "open") {
    votingOpen = false;
    closedReason =
      election.status === "certified"
        ? "This election has been certified and is no longer accepting ballots."
        : "This election is closed.";
  } else if (election.startAt && election.startAt.getTime() > now) {
    votingOpen = false;
    closedReason = "Voting has not opened yet.";
  } else if (election.endAt && election.endAt.getTime() < now) {
    votingOpen = false;
    closedReason = "The voting window has ended.";
  }

  const positions =
    (election.get("positions") as ElectionPosition[] | undefined) ?? [];

  return {
    id: election.id,
    title: election.title,
    description: election.description,
    slug: election.slug,
    status: election.status as "open" | "closed" | "certified",
    ballotSecrecy: election.ballotSecrecy,
    votingMethod: election.votingMethod,
    digitalSignatureRequired: election.digitalSignatureRequired,
    verificationMethod:
      election.securityConfig?.verificationMethod ?? "email",
    resultsVisibility: election.resultsVisibility,
    startAt: election.startAt,
    endAt: election.endAt,
    votingOpen,
    closedReason,
    openEnrollment: election.openEnrollment,
    positions: positions
      .sort((a, b) => a.position - b.position)
      .map((position) => {
        const candidates =
          (position.get("candidates") as Candidate[] | undefined) ?? [];
        return {
          id: position.id,
          title: position.title,
          positionType: position.positionType,
          seatsAvailable: position.seatsAvailable,
          candidates: candidates
            .filter((candidate) => candidate.status === "approved")
            .sort((a, b) => a.position - b.position)
            .map((candidate) => {
              const qas =
                (candidate.get("qas") as CandidateQA[] | undefined) ?? [];
              return {
                id: candidate.id,
                name: candidate.name,
                biography: candidate.biography,
                goalsStatement: candidate.goalsStatement,
                photoUrl: candidate.photoUrl,
                qas: qas
                  .sort((a, b) => a.position - b.position)
                  .map((qa) => ({
                    id: qa.id,
                    question: qa.question,
                    answer: qa.answer,
                  })),
              };
            }),
        };
      }),
  };
}
