import getCandidateCallStatus from '../getCandidateCallStatus';
import getOutcome from './getOutcome';
import getWinningParty from './getWinningParty';
import getWinningCandidate from './getWinningCandidate';
import getOutcomeBalanceType from './getOutcomeBalanceType';

/**
 * Utility to get the current status object for a contest. The status object
 * consists of the contest's tabulation status, its recount status, its outcome
 * status, and, when relevant, its winning party.
 *
 * @param {ContestMetadata} contestMetadata
 *   The metadata of the contest for which we want the status
 * @param {ContestSummaryResults} contestSummary
 *   The summary-level results of the contest for which we want the status
 * @returns {ContestStatus}
 */
const getContestStatus = function getContestStatus(contestMetadata = {}, candidates = [], contestSummary = {}) {
  const {
    civicContestMetadata: {
      holdingParty
    } = {}
  } = contestMetadata;
  const {
    callsByParty = {},
    results = [],
    status: {
      tabulation,
      recount
    } = {}
  } = contestSummary;
  const candidatesById = Object.fromEntries(candidates.map(candidate => [candidate.id, candidate]));
  const candidateCallStatus = results.reduce((callStatus, candidate) => {
    const candidateStatus = getCandidateCallStatus(contestSummary, candidate.id);
    const candidateMeta = candidatesById[candidate.id];
    if (!candidateMeta) return callStatus;
    const newStatus = {
      ...callStatus
    };
    if (candidateStatus === 'winner') {
      newStatus.hasWinner = true;
      newStatus.winningParties.push(candidateMeta.party);
      newStatus.winningCandidates.push(candidate.id);
    }
    if (candidateStatus === 'runoff') {
      newStatus.hasRunoff = true;
      newStatus.runoffParties.push(candidateMeta.party);
      newStatus.runoffCandidates.push(candidate.id);
    }
    if (candidate.votes === callStatus.maxCandidateVotes && candidate.votes > 0) {
      newStatus.isTied = true;
      newStatus.leadingParty = undefined;
      newStatus.leadingCandidate = undefined;
    }
    if (candidate.votes > callStatus.maxCandidateVotes) {
      newStatus.leadingParty = candidateMeta.party;
      newStatus.leadingCandidate = candidateMeta.id;
      newStatus.maxCandidateVotes = candidate.votes;
    }
    return newStatus;
  }, {
    hasWinner: false,
    hasRunoff: false,
    winningParties: [],
    winningCandidates: [],
    runoffParties: [],
    runoffCandidates: [],
    maxCandidateVotes: 0,
    leadingParty: undefined,
    leadingCandidate: undefined,
    isTied: false
  });
  const winningPartiesFromPartyCalls = Object.entries(callsByParty).filter(entry => entry[1].status === 'winner').map(([party]) => party);
  return {
    tabulation,
    recount,
    outcome: getOutcome({
      candidateCallStatus,
      winningPartiesFromPartyCalls
    }),
    winningCandidate: getWinningCandidate({
      candidateCallStatus
    }),
    winningParty: getWinningParty({
      candidateCallStatus,
      winningPartiesFromPartyCalls
    }),
    outcomeBalanceType: getOutcomeBalanceType({
      holdingParty,
      candidateCallStatus,
      winningPartiesFromPartyCalls
    }),
    leadingParty: candidateCallStatus.leadingParty,
    leadingCandidate: candidateCallStatus.leadingCandidate,
    isTied: candidateCallStatus.isTied
  };
};
export default getContestStatus;