import { Op } from "sequelize";
import { initDb } from "@/lib/db";
import type {
  AuthorizationRecordStatus,
  EligibilityStatus,
  VoterType,
} from "@/types";

export interface VoterRollRow {
  id: string;
  voterType: VoterType;
  voterRefId: string;
  label: string;
  secondaryLabel: string | null;
  weight: number;
  eligibilityStatus: EligibilityStatus;
  hasVoted: boolean;
  votedAt: Date | null;
  ownerCount: number;
  authorizationStatus: AuthorizationRecordStatus | null;
  designatedVoterName: string | null;
  designatedVoterContactId: string | null;
}

export interface VoterRollSummary {
  total: number;
  eligible: number;
  pendingAuthorization: number;
  blocked: number;
  voted: number;
}

export async function getVoterRoll(
  electionId: string,
  workspaceId: string,
  filters: {
    eligibilityStatus?: EligibilityStatus | "all";
    search?: string;
  } = {}
): Promise<{ rows: VoterRollRow[]; summary: VoterRollSummary }> {
  const {
    Election,
    ElectionVoterRoll,
    Contact,
    VotingEntity,
    VotingEntityOwner,
    AuthorizationRecord,
  } = initDb();

  const election = await Election.findOne({
    where: { id: electionId, workspaceId },
  });
  if (!election) {
    return {
      rows: [],
      summary: {
        total: 0,
        eligible: 0,
        pendingAuthorization: 0,
        blocked: 0,
        voted: 0,
      },
    };
  }

  const where: Record<string, unknown> = { electionId };
  if (
    filters.eligibilityStatus &&
    filters.eligibilityStatus !== "all"
  ) {
    where.eligibilityStatus = filters.eligibilityStatus;
  }

  const roll = await ElectionVoterRoll.findAll({
    where,
    order: [["createdAt", "ASC"]],
  });

  const contactIds = roll
    .filter((row) => row.voterType === "contact")
    .map((row) => row.voterRefId);
  const entityIds = roll
    .filter((row) => row.voterType === "voting_entity")
    .map((row) => row.voterRefId);

  const [contacts, entities, owners, authRecords] = await Promise.all([
    contactIds.length
      ? Contact.findAll({
          where: { id: contactIds, workspaceId },
          attributes: ["id", "name", "email"],
        })
      : Promise.resolve([]),
    entityIds.length
      ? VotingEntity.findAll({
          where: { id: entityIds, workspaceId },
          attributes: ["id", "label", "entityType"],
        })
      : Promise.resolve([]),
    entityIds.length
      ? VotingEntityOwner.findAll({
          where: { votingEntityId: entityIds },
          attributes: ["votingEntityId", "contactId", "isPrimary"],
        })
      : Promise.resolve([]),
    entityIds.length
      ? AuthorizationRecord.findAll({
          where: {
            votingEntityId: entityIds,
            status: { [Op.ne]: "superseded" },
          },
          order: [["createdAt", "DESC"]],
        })
      : Promise.resolve([]),
  ]);

  const contactMap = new Map(contacts.map((contact) => [contact.id, contact]));
  const entityMap = new Map(entities.map((entity) => [entity.id, entity]));
  const ownerCountMap = new Map<string, number>();
  for (const owner of owners) {
    ownerCountMap.set(
      owner.votingEntityId,
      (ownerCountMap.get(owner.votingEntityId) ?? 0) + 1
    );
  }

  const latestAuthByEntity = new Map<
    string,
    (typeof authRecords)[number]
  >();
  for (const record of authRecords) {
    if (!latestAuthByEntity.has(record.votingEntityId)) {
      latestAuthByEntity.set(record.votingEntityId, record);
    }
  }

  const designatedIds = Array.from(latestAuthByEntity.values())
    .map((record) => record.designatedVoterContactId)
    .filter((id): id is string => Boolean(id));

  const designatedContacts =
    designatedIds.length > 0
      ? await Contact.findAll({
          where: { id: designatedIds, workspaceId },
          attributes: ["id", "name"],
        })
      : [];
  const designatedMap = new Map(
    designatedContacts.map((contact) => [contact.id, contact.name])
  );

  const search = filters.search?.trim().toLowerCase() ?? "";

  const rows: VoterRollRow[] = [];
  for (const row of roll) {
    let label = "Unknown";
    let secondaryLabel: string | null = null;
    let ownerCount = 0;
    let authorizationStatus: AuthorizationRecordStatus | null = null;
    let designatedVoterName: string | null = null;
    let designatedVoterContactId: string | null = null;

    if (row.voterType === "contact") {
      const contact = contactMap.get(row.voterRefId);
      label = contact?.name ?? "Contact";
      secondaryLabel = contact?.email ?? null;
    } else if (row.voterType === "voting_entity") {
      const entity = entityMap.get(row.voterRefId);
      label = entity?.label ?? "Voting entity";
      secondaryLabel = entity?.entityType ?? null;
      ownerCount = ownerCountMap.get(row.voterRefId) ?? 0;
      const auth = latestAuthByEntity.get(row.voterRefId);
      if (auth) {
        authorizationStatus = auth.status;
        designatedVoterContactId = auth.designatedVoterContactId;
        designatedVoterName = auth.designatedVoterContactId
          ? designatedMap.get(auth.designatedVoterContactId) ?? null
          : null;
      }
    }

    if (
      search &&
      !label.toLowerCase().includes(search) &&
      !(secondaryLabel?.toLowerCase().includes(search) ?? false)
    ) {
      continue;
    }

    rows.push({
      id: row.id,
      voterType: row.voterType,
      voterRefId: row.voterRefId,
      label,
      secondaryLabel,
      weight: Number(row.weight),
      eligibilityStatus: row.eligibilityStatus,
      hasVoted: row.hasVoted,
      votedAt: row.votedAt,
      ownerCount,
      authorizationStatus,
      designatedVoterName,
      designatedVoterContactId,
    });
  }

  const allForSummary = await ElectionVoterRoll.findAll({
    where: { electionId },
    attributes: ["eligibilityStatus", "hasVoted"],
  });

  const summary: VoterRollSummary = {
    total: allForSummary.length,
    eligible: allForSummary.filter((row) => row.eligibilityStatus === "eligible")
      .length,
    pendingAuthorization: allForSummary.filter(
      (row) => row.eligibilityStatus === "pending_authorization"
    ).length,
    blocked: allForSummary.filter((row) => row.eligibilityStatus === "blocked")
      .length,
    voted: allForSummary.filter((row) => row.hasVoted).length,
  };

  return { rows, summary };
}

export async function getWorkspaceVotingEntities(workspaceId: string) {
  const { VotingEntity, VotingEntityOwner, Contact, AuthorizationRecord } =
    initDb();

  const entities = await VotingEntity.findAll({
    where: { workspaceId, isActive: true },
    order: [["label", "ASC"]],
    include: [
      {
        model: VotingEntityOwner,
        as: "owners",
        include: [
          {
            model: Contact,
            as: "contact",
            attributes: ["id", "name", "email"],
          },
        ],
      },
      {
        model: AuthorizationRecord,
        as: "authorizationRecords",
        required: false,
        separate: true,
        order: [["createdAt", "DESC"]],
        limit: 1,
      },
    ],
  });

  return entities.map((entity) => {
    const owners =
      (entity.get("owners") as
        | {
            contactId: string;
            isPrimary: boolean;
            contact?: { id: string; name: string; email: string };
          }[]
        | undefined) ?? [];
    const latestAuth = (
      entity.get("authorizationRecords") as
        | { status: AuthorizationRecordStatus; designatedVoterContactId: string | null }[]
        | undefined
    )?.[0];

    return {
      id: entity.id,
      label: entity.label,
      entityType: entity.entityType,
      metadata: entity.metadata,
      ownerCount: owners.length,
      owners: owners.map((owner) => ({
        contactId: owner.contactId,
        isPrimary: owner.isPrimary,
        name: owner.contact?.name ?? "Unknown",
        email: owner.contact?.email ?? "",
      })),
      latestAuthorizationStatus: latestAuth?.status ?? null,
      designatedVoterContactId: latestAuth?.designatedVoterContactId ?? null,
    };
  });
}
