"use server";

import { revalidatePath } from "next/cache";
import type { Transaction } from "sequelize";
import { initDb } from "@/lib/db";
import { requireClientWorkspace } from "@/lib/auth/require-auth";
import {
  reviewAuthorizationRecordSchema,
  upsertAuthorizationRecordSchema,
} from "@/lib/validation/authorization-record";
import type { ActionResult } from "@/lib/actions/auth";
import type { AuthorizationRecordStatus } from "@/types";

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

async function getScopedEntity(votingEntityId: string, workspaceId: string) {
  const { VotingEntity } = initDb();
  return VotingEntity.findOne({ where: { id: votingEntityId, workspaceId } });
}

async function latestRecord(votingEntityId: string, transaction?: Transaction) {
  const { AuthorizationRecord } = initDb();
  return AuthorizationRecord.findOne({
    where: {
      votingEntityId,
      status: ["valid", "missing", "rejected"],
    },
    order: [["createdAt", "DESC"]],
    transaction,
  });
}

async function insertSupersedingRecord(input: {
  votingEntityId: string;
  status: AuthorizationRecordStatus;
  designatedVoterContactId: string | null;
  reasonRequired: string | null;
  documentFileUrl: string | null;
  dateReceived: string | null;
  effectiveDate: string | null;
  expirationDate: string | null;
  signedDate: string | null;
  rejectionReason: string | null;
  entityTypeAtFiling: string | null;
  createdBy: string;
  transaction: Transaction;
}): Promise<string> {
  const { AuthorizationRecord } = initDb();
  const previous = await latestRecord(input.votingEntityId, input.transaction);

  const created = await AuthorizationRecord.create(
    {
      votingEntityId: input.votingEntityId,
      required: true,
      reasonRequired: input.reasonRequired,
      designatedVoterContactId: input.designatedVoterContactId,
      entityTypeAtFiling: input.entityTypeAtFiling,
      documentFileUrl: input.documentFileUrl,
      dateReceived: input.dateReceived,
      status: input.status,
      effectiveDate: input.effectiveDate,
      expirationDate: input.expirationDate,
      signedDate: input.signedDate,
      supersedesRecordId: previous?.id ?? null,
      rejectionReason: input.rejectionReason,
      createdBy: input.createdBy,
    },
    { transaction: input.transaction }
  );

  if (previous && previous.status !== "superseded") {
    await previous.update(
      { status: "superseded" },
      { transaction: input.transaction }
    );
  }

  return created.id;
}

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

  const entity = await getScopedEntity(parsed.data.votingEntityId, workspaceId);
  if (!entity) return { success: false, error: "Voting entity not found" };

  if (parsed.data.designatedVoterContactId) {
    const { Contact, VotingEntityOwner } = initDb();
    const contact = await Contact.findOne({
      where: { id: parsed.data.designatedVoterContactId, workspaceId },
    });
    if (!contact) {
      return { success: false, error: "Designated voter contact not found" };
    }
    const owner = await VotingEntityOwner.findOne({
      where: {
        votingEntityId: entity.id,
        contactId: contact.id,
      },
    });
    if (!owner) {
      return {
        success: false,
        error: "Designated voter must be an owner of this entity",
      };
    }
  }

  const { sequelize, AuditLog } = initDb();

  try {
    const authorizationRecordId = await sequelize.transaction(async (t) => {
      const id = await insertSupersedingRecord({
        votingEntityId: entity.id,
        status: parsed.data.status,
        designatedVoterContactId: parsed.data.designatedVoterContactId ?? null,
        reasonRequired: parsed.data.reasonRequired ?? null,
        documentFileUrl: parsed.data.documentFileUrl ?? null,
        dateReceived: parsed.data.dateReceived,
        effectiveDate: parsed.data.effectiveDate,
        expirationDate: parsed.data.expirationDate,
        signedDate: parsed.data.signedDate,
        rejectionReason: null,
        entityTypeAtFiling: entity.entityType,
        createdBy: userId,
        transaction: t,
      });

      await AuditLog.create(
        {
          actorUserId: userId,
          workspaceId,
          action: "authorization_record.upserted",
          metadata: {
            votingEntityId: entity.id,
            authorizationRecordId: id,
            status: parsed.data.status,
          },
        },
        { transaction: t }
      );

      return id;
    });

    revalidatePath("/app/elections");
    return { success: true, data: { authorizationRecordId } };
  } catch {
    return { success: false, error: "Failed to save authorization record" };
  }
}

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

  const entity = await getScopedEntity(parsed.data.votingEntityId, workspaceId);
  if (!entity) return { success: false, error: "Voting entity not found" };

  const { sequelize, AuditLog } = initDb();
  const previous = await latestRecord(entity.id);
  if (!previous) {
    return {
      success: false,
      error: "No authorization record exists to review for this entity",
    };
  }

  const status: AuthorizationRecordStatus =
    parsed.data.decision === "approve" ? "valid" : "rejected";

  try {
    const authorizationRecordId = await sequelize.transaction(async (t) => {
      const id = await insertSupersedingRecord({
        votingEntityId: entity.id,
        status,
        designatedVoterContactId:
          parsed.data.designatedVoterContactId ??
          previous.designatedVoterContactId,
        reasonRequired: previous.reasonRequired,
        documentFileUrl: previous.documentFileUrl,
        dateReceived: previous.dateReceived,
        effectiveDate: parsed.data.effectiveDate ?? previous.effectiveDate,
        expirationDate: parsed.data.expirationDate ?? previous.expirationDate,
        signedDate: previous.signedDate,
        rejectionReason:
          parsed.data.decision === "reject"
            ? parsed.data.rejectionReason ?? null
            : null,
        entityTypeAtFiling: previous.entityTypeAtFiling ?? entity.entityType,
        createdBy: userId,
        transaction: t,
      });

      await AuditLog.create(
        {
          actorUserId: userId,
          workspaceId,
          action: "authorization_record.reviewed",
          metadata: {
            votingEntityId: entity.id,
            authorizationRecordId: id,
            decision: parsed.data.decision,
            rejectionReason: parsed.data.rejectionReason ?? null,
          },
        },
        { transaction: t }
      );

      return id;
    });

    revalidatePath("/app/elections");
    return { success: true, data: { authorizationRecordId } };
  } catch {
    return { success: false, error: "Failed to review authorization record" };
  }
}
