import { useMemo } from 'react';
import sortBy from "../../../utils/sorting/sortBy.js";
import { useContestCollectionMetadata } from "../../../hooks/election/index.js";
import { FIRST_AD_GROUP_SIZE, MIN_AD_GROUP_SIZE, SECOND_AD_GROUP_SIZE } from './constants';

/**
 * @parm {object} opts
 * @param {string} opts.collectionId
 * @param {{ districtNumber: string, note: string }[]} [opts.notes]
 * @param {{ districtNumber: string, reporterNote: object }[]} [opts.annotations]
 * @param {string[]} [opts.featuredDistricts]
 */
const useAdSections = opts => {
  const {
    collectionId,
    notes = [],
    annotations = [],
    featuredDistricts = []
  } = opts;
  const collectionMetadata = useContestCollectionMetadata(collectionId);
  const annotationsByDistrict = useMemo(() => annotations.reduce((byDistrict, annotation) => {
    byDistrict.set(annotation.districtNumber, annotation.reporterNote);
    return byDistrict;
  }, new Map()), [annotations]);
  const {
    stateFips: fips,
    sortedBlocks: districtBlocks
  } = useMemo(() => {
    const contests = Object.values(collectionMetadata || {});
    let stateFips;
    let layout;
    const districts = new Set();
    const notesByKey = notes.reduce((byKey, note) => {
      districts.add(note.districtNumber);
      const key = `${note.districtNumber}-${note.party}`;
      byKey.set(key, note.note);
      return byKey;
    }, new Map());
    const contestMap = contests.reduce((map, contest) => {
      // Guard against mismatched state FIPS code
      if (stateFips && stateFips !== contest.stateFips) {
        // eslint-disable-next-line no-console
        console.warn('StateHouseResults: Ignoring contest with mismatched state FIPS code' + ` (${contest.id} with FIPS ${contest.stateFips})`);
        return map;
      }

      // Guard against mismatched layout
      const incorrectParty = layout === 'party-primary' && !contest.party || layout === 'all-party' && !!contest.party;
      if (layout && incorrectParty) {
        // eslint-disable-next-line no-console
        console.warn('StateHouseResults: Ignoring contest with mismatched party for' + ` layout (${contest.id} with party ${contest.party})`);
        return map;
      }
      districts.add(contest.districtNumber);
      stateFips = contest.stateFips;
      layout = contest.party ? 'party-primary' : 'all-party';
      const districtContests = map.get(contest.districtNumber) || new Map();
      if (districtContests.has(contest.party)) {
        // eslint-disable-next-line no-console
        console.warn('StateHouseResults: Ignoring duplicate contest in district' + ` ${contest.districtNumber} with party ${contest.party}`);
        return map;
      }
      districtContests.set(contest.party, contest.id);
      map.set(contest.districtNumber, districtContests);
      return map;
    }, new Map());
    const getContestId = (districtNumber, party) => {
      const districtContests = contestMap.get(districtNumber);
      if (!districtContests) return undefined;
      return districtContests.get(party);
    };
    const blocks = [...districts].map(districtNumber => {
      const partyBlocks = ['dem', 'gop'].map(party => ({
        id: party,
        party,
        note: notesByKey.get(`${districtNumber}-${party}`),
        contestId: getContestId(districtNumber, party)
      }));
      const allPartyBlocks = [{
        id: 'all-party',
        note: notesByKey.get(`${districtNumber}-undefined`),
        contestId: getContestId(districtNumber, undefined)
      }];
      return {
        stateFips,
        layout,
        districtNumber,
        annotation: annotationsByDistrict.get(districtNumber),
        blocks: layout === 'party-primary' ? partyBlocks : allPartyBlocks
      };
    });
    const sortedBlocks = sortBy(blocks, ['districtNumber']);
    return {
      stateFips,
      sortedBlocks
    };
  }, [collectionMetadata, notes, annotationsByDistrict]);
  const sections = useMemo(() => {
    const grouped = districtBlocks.reduce((groups, district, i, allDistricts) => {
      const groupSize = groups.length < 2 ? FIRST_AD_GROUP_SIZE : SECOND_AD_GROUP_SIZE;
      const emptyGroup = {
        id: `group-${groups.length}`,
        districts: []
      };
      if (i === 0) groups.push(emptyGroup);
      const isFull = groups[groups.length - 1].districts.length >= groupSize;
      const enoughLeft = allDistricts.length >= MIN_AD_GROUP_SIZE;
      if (isFull && enoughLeft) groups.push(emptyGroup);
      groups[groups.length - 1].districts.push(district);
      return groups;
    }, []);
    return grouped;
  }, [districtBlocks]);
  const navDistricts = useMemo(() => {
    const dists = new Set(featuredDistricts);
    annotations.forEach(a => {
      dists.add(a.districtNumber);
    });
    return {
      stateFips: fips,
      districtNumbers: sortBy([...dists])
    };
  }, [fips, annotations, featuredDistricts]);
  return {
    sections,
    navDistricts
  };
};
export default useAdSections;