"use server";

import { revalidatePath } from "next/cache";
import { initDb } from "@/lib/db";
import { requireClientWorkspace } from "@/lib/auth/require-auth";
import {
  addCandidateSchema,
  addPositionSchema,
  candidateIdSchema,
  upsertCandidateQASchema,
} from "@/lib/validation/candidate";
import { getScopedElection } from "@/lib/queries/election-queries";
import { getPositionForWorkspace } from "@/lib/queries/candidate-queries";
import type { ActionResult } from "@/lib/actions/auth";

function firstZodError(error: { issues: { message: string }[] }): string {
  return error.issues[0]?.message ?? "Invalid input";
}

function revalidateElection(electionId: string, slug?: string | null) {
  revalidatePath(`/app/elections/${electionId}`);
  revalidatePath("/app/elections");
  if (slug) revalidatePath(`/e/${slug}`);
}

export async function addPosition(
  input: unknown
): Promise<ActionResult<{ positionId: string }>> {
  const { workspaceId } = await requireClientWorkspace();
  const parsed = addPositionSchema.safeParse(input);
  if (!parsed.success) return { success: false, error: firstZodError(parsed.error) };

  const election = await getScopedElection(parsed.data.electionId, workspaceId);
  if (!election) return { success: false, error: "Election not found" };
  if (election.status !== "draft") {
    return { success: false, error: "Positions can only be added to draft elections" };
  }

  const { ElectionPosition } = initDb();
  const maxPos = await ElectionPosition.max("position", {
    where: { electionId: election.id },
  });
  const nextPosition = typeof maxPos === "number" ? maxPos + 1 : 0;
  const seats =
    parsed.data.positionType === "multi_seat" ? parsed.data.seatsAvailable : 1;

  const position = await ElectionPosition.create({
    electionId: election.id,
    title: parsed.data.title,
    positionType: parsed.data.positionType,
    seatsAvailable: seats,
    position: nextPosition,
  });

  revalidateElection(election.id, election.slug);
  return { success: true, data: { positionId: position.id } };
}

export async function addCandidate(
  input: unknown
): Promise<ActionResult<{ candidateId: string }>> {
  const { workspaceId } = await requireClientWorkspace();
  const parsed = addCandidateSchema.safeParse(input);
  if (!parsed.success) return { success: false, error: firstZodError(parsed.error) };

  const scoped = await getPositionForWorkspace(
    parsed.data.electionPositionId,
    workspaceId
  );
  if (!scoped) return { success: false, error: "Position not found" };
  if (scoped.election.status !== "draft") {
    return { success: false, error: "Candidates can only be added to draft elections" };
  }

  const { Candidate } = initDb();
  const maxPos = await Candidate.max("position", {
    where: { electionPositionId: scoped.position.id },
  });
  const nextPosition = typeof maxPos === "number" ? maxPos + 1 : 0;

  const candidate = await Candidate.create({
    electionPositionId: scoped.position.id,
    name: parsed.data.name,
    biography: parsed.data.biography ?? null,
    goalsStatement: parsed.data.goalsStatement ?? null,
    conflictOfInterestDisclosure:
      parsed.data.conflictOfInterestDisclosure ?? null,
    photoUrl: parsed.data.photoUrl ?? null,
    resumeUrl: parsed.data.resumeUrl ?? null,
    status: "pending",
    position: nextPosition,
  });

  revalidateElection(scoped.election.id, scoped.election.slug);
  return { success: true, data: { candidateId: candidate.id } };
}

export async function approveCandidate(
  candidateId: string
): Promise<ActionResult> {
  return setCandidateStatus(candidateId, "approved");
}

export async function withdrawCandidate(
  candidateId: string
): Promise<ActionResult> {
  return setCandidateStatus(candidateId, "withdrawn");
}

async function setCandidateStatus(
  candidateId: string,
  status: "approved" | "withdrawn"
): Promise<ActionResult> {
  const { workspaceId } = await requireClientWorkspace();
  const parsed = candidateIdSchema.safeParse({ candidateId });
  if (!parsed.success) return { success: false, error: "Invalid candidate" };

  const { Candidate, ElectionPosition, Election } = initDb();
  const candidate = await Candidate.findByPk(candidateId, {
    include: [
      {
        model: ElectionPosition,
        as: "electionPosition",
        include: [{ model: Election, as: "election" }],
      },
    ],
  });
  if (!candidate) return { success: false, error: "Candidate not found" };

  const position = candidate.get("electionPosition") as
    | { election?: { id: string; slug: string; workspaceId: string } }
    | undefined;
  const election = position?.election;
  if (!election || election.workspaceId !== workspaceId) {
    return { success: false, error: "Candidate not found" };
  }

  await candidate.update({ status });
  revalidateElection(election.id, election.slug);
  return { success: true };
}

export async function upsertCandidateQA(
  input: unknown
): Promise<ActionResult<{ qaId: string }>> {
  const { workspaceId } = await requireClientWorkspace();
  const parsed = upsertCandidateQASchema.safeParse(input);
  if (!parsed.success) return { success: false, error: firstZodError(parsed.error) };

  const { Candidate, CandidateQA, ElectionPosition, Election } = initDb();
  const candidate = await Candidate.findByPk(parsed.data.candidateId, {
    include: [
      {
        model: ElectionPosition,
        as: "electionPosition",
        include: [{ model: Election, as: "election" }],
      },
    ],
  });
  if (!candidate) return { success: false, error: "Candidate not found" };

  const position = candidate.get("electionPosition") as
    | { election?: { id: string; slug: string; workspaceId: string; status: string } }
    | undefined;
  const election = position?.election;
  if (!election || election.workspaceId !== workspaceId) {
    return { success: false, error: "Candidate not found" };
  }

  if (parsed.data.qaId) {
    const existing = await CandidateQA.findOne({
      where: { id: parsed.data.qaId, candidateId: candidate.id },
    });
    if (!existing) return { success: false, error: "Q&A not found" };
    await existing.update({
      question: parsed.data.question,
      answer: parsed.data.answer,
    });
    revalidateElection(election.id, election.slug);
    return { success: true, data: { qaId: existing.id } };
  }

  const maxPos = await CandidateQA.max("position", {
    where: { candidateId: candidate.id },
  });
  const qa = await CandidateQA.create({
    candidateId: candidate.id,
    question: parsed.data.question,
    answer: parsed.data.answer,
    position: typeof maxPos === "number" ? maxPos + 1 : 0,
  });

  revalidateElection(election.id, election.slug);
  return { success: true, data: { qaId: qa.id } };
}
