import { Op } from "sequelize";
import { initDb } from "@/lib/db";
import { getElectionPositions } from "@/lib/queries/candidate-queries";
import type { ElectionPositionType } from "@/types";

export interface CandidateTally {
  candidateId: string;
  name: string;
  count: number;
  percent: number;
}

export interface PositionTally {
  positionId: string;
  title: string;
  positionType: ElectionPositionType;
  seatsAvailable: number;
  totalSelections: number;
  candidates: CandidateTally[];
}

export interface BallotResults {
  ballotCount: number;
  positions: PositionTally[];
}

/**
 * Tallies BallotSelections only. Never joins Ballots back to ElectionVoterRoll.
 */
export async function getBallotResults(
  electionId: string
): Promise<BallotResults> {
  const { Ballot, BallotSelection } = initDb();
  const positions = await getElectionPositions(electionId);

  const ballotCount = await Ballot.count({ where: { electionId } });
  const ballots = await Ballot.findAll({
    where: { electionId },
    attributes: ["id"],
  });
  const ballotIds = ballots.map((ballot) => ballot.id);

  const selections =
    ballotIds.length === 0
      ? []
      : await BallotSelection.findAll({
          where: { ballotId: { [Op.in]: ballotIds } },
        });

  const counts = new Map<string, number>();
  const totals = new Map<string, number>();

  for (const selection of selections) {
    if (!selection.candidateId) continue;
    const key = `${selection.electionPositionId}:${selection.candidateId}`;
    counts.set(key, (counts.get(key) ?? 0) + 1);
    totals.set(
      selection.electionPositionId,
      (totals.get(selection.electionPositionId) ?? 0) + 1
    );
  }

  return {
    ballotCount,
    positions: positions.map((position) => {
      const totalSelections = totals.get(position.id) ?? 0;
      return {
        positionId: position.id,
        title: position.title,
        positionType: position.positionType,
        seatsAvailable: position.seatsAvailable,
        totalSelections,
        candidates: position.candidates.map((candidate) => {
          const count = counts.get(`${position.id}:${candidate.id}`) ?? 0;
          const percent =
            totalSelections === 0
              ? 0
              : Math.round((count / totalSelections) * 1000) / 10;
          return {
            candidateId: candidate.id,
            name: candidate.name,
            count,
            percent,
          };
        }),
      };
    }),
  };
}

export function canRevealResults(input: {
  status: string;
  resultsVisibility: string;
}): boolean {
  if (input.resultsVisibility === "admin_realtime") return true;
  return input.status === "closed" || input.status === "certified";
}
