import { createHash } from "crypto";
import { hashPassword } from "@pmap/auth";
import { prisma } from "@pmap/database";
import { getAdminSessionFromRequest } from "../../../../auth";
import { fail, ok } from "../../_lib";
import { findOrCreateParticipantCategoryRate, getParticipantCategoryAmount } from "./_categories";
import { fallbackParticipantDefaultPassword, getPaidCompedParticipantDefaultPassword, shouldUsePaidCompedDefaultPassword } from "./_default-password";
import { sendParticipantQrEmail } from "./_mailer";
import { createQrSvg } from "./_qr";
import { generateParticipantReference } from "./_reference";

type ParticipantPayload = {
  id?: string;
  Reference?: string;
  Name?: string;
  "First Name"?: string;
  "Middle Name"?: string;
  "Last Name"?: string;
  "Mobile Number"?: string;
  Email?: string;
  Company?: string;
  Category?: string;
  Registration?: string;
  Payment?: string;
  QR?: string;
  Chapter?: string;
  chapterId?: string;
};

function normalizeStatus(value: string | undefined, fallback: string) {
  return (value || fallback).trim().toUpperCase().replaceAll(" ", "_").replaceAll("-", "_");
}

function displayStatus(value: string | null | undefined) {
  if (!value) return "";
  return value.toLowerCase().replaceAll("_", " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
}

function displayRegistrationStatus(value: string | null | undefined) {
  const status = displayStatus(value);
  return status === "Verified" ? "Confirmed" : status;
}

function splitName(name: string | undefined) {
  const parts = (name || "").trim().split(/\s+/).filter(Boolean);
  if (parts.length === 0) return { firstName: "", lastName: "" };
  if (parts.length === 1) return { firstName: parts[0] ?? "New", lastName: "Participant" };
  return {
    firstName: parts[0] ?? "New",
    lastName: parts.slice(1).join(" ")
  };
}

function participantNameParts(payload: ParticipantPayload) {
  const firstName = payload["First Name"]?.trim();
  const middleName = payload["Middle Name"]?.trim();
  const lastName = payload["Last Name"]?.trim();
  if (firstName || lastName) {
    return {
      firstName: firstName || "New",
      middleName: middleName || null,
      lastName: lastName || "Participant"
    };
  }
  return { ...splitName(payload.Name), middleName: null };
}

function tokenHash(seed: string) {
  return createHash("sha256").update(seed).digest("hex");
}

async function toRow(participant: {
  id: string;
  registrationReference: string;
  firstName: string;
  middleName: string | null;
  lastName: string;
  mobileNumber: string | null;
  company: string | null;
  participantCategory: string;
  chapter: { id: string; name: string } | null;
  user: { email: string };
  registrations: Array<{ status: string; paymentStatus: string; totalAmount: unknown; currency: string }>;
  qrCredentials: Array<{ id: string; tokenHash: string; issuedAt: Date; isActive: boolean; revokedAt: Date | null }>;
}) {
  const registration = participant.registrations[0];
  const hasActiveQr = participant.qrCredentials.some((credential) => credential.isActive && !credential.revokedAt);
  const amount = Number(registration?.totalAmount ?? await getParticipantCategoryAmount(participant.participantCategory));
  const currency = registration?.currency ?? "PHP";
  return {
    id: participant.id,
    Reference: participant.registrationReference,
    Name: [participant.firstName, participant.middleName, participant.lastName].filter(Boolean).join(" "),
    "First Name": participant.firstName,
    "Middle Name": participant.middleName ?? "",
    "Last Name": participant.lastName,
    "Mobile Number": participant.mobileNumber ?? "",
    Email: participant.user.email,
    Company: participant.company ?? "",
    Category: participant.participantCategory,
    Amount: `${currency} ${amount.toLocaleString("en-PH", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`,
    Chapter: participant.chapter?.name ?? "",
    chapterId: participant.chapter?.id ?? "",
    Registration: displayRegistrationStatus(registration?.status) || "Pending",
    Payment: displayStatus(registration?.paymentStatus) || "Pending",
    QR: hasActiveQr ? "Issued" : "Not Ready",
    Actions: "View / Edit"
  };
}

async function listParticipants() {
  const participants = await prisma.participantProfile.findMany({
    include: {
      user: { select: { email: true } },
      chapter: { select: { id: true, name: true } },
      registrations: { orderBy: { submittedAt: "desc" }, take: 1 },
      qrCredentials: { where: { isActive: true }, orderBy: { issuedAt: "desc" }, take: 1 }
    },
    orderBy: [{ createdAt: "desc" }, { lastName: "asc" }],
    take: 1000
  });
  return Promise.all(participants.map((participant) => toRow(participant)));
}

export async function GET(request: Request) {
  try {
    if (!await getAdminSessionFromRequest(request)) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    return ok(request, await listParticipants());
  } catch (error) {
    return fail(request, "PARTICIPANTS_UNAVAILABLE", "Participants could not be loaded.", 500, error instanceof Error ? error.message : error);
  }
}

export async function POST(request: Request) {
  try {
    if (!await getAdminSessionFromRequest(request)) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    const payload = await request.json() as ParticipantPayload;
    const { firstName, middleName, lastName } = participantNameParts(payload);
    if (!firstName.trim() || !lastName.trim()) return fail(request, "NAME_REQUIRED", "First name and last name are required.", 422);
    const reference = await generateParticipantReference();
    const email = payload.Email?.trim().toLowerCase();
    if (!email) return fail(request, "EMAIL_REQUIRED", "Email is required.");
    const category = payload.Category?.trim() || "Guest Only";
    const chapterId = category === "Chapter Delegate" ? payload.chapterId || null : null;
    const categoryAmount = await getParticipantCategoryAmount(category);
    const rate = await findOrCreateParticipantCategoryRate(category, categoryAmount);
    if (!rate) return fail(request, "REGISTRATION_RATE_REQUIRED", "Create a registration period before adding participants.", 409);
    const defaultPassword = shouldUsePaidCompedDefaultPassword(payload.Payment)
      ? await getPaidCompedParticipantDefaultPassword()
      : fallbackParticipantDefaultPassword();

    const row = await prisma.$transaction(async (tx) => {
      const user = await tx.user.create({
        data: {
          email,
          passwordHash: await hashPassword(defaultPassword),
          emailVerifiedAt: new Date()
        }
      });

      const participant = await tx.participantProfile.create({
        data: {
          userId: user.id,
          registrationReference: reference,
          firstName,
          middleName,
          lastName,
          mobileNumber: payload["Mobile Number"]?.trim() || null,
          company: payload.Company?.trim() || null,
          participantCategory: category,
          chapterId,
          verificationStatus: "PENDING"
        }
      });

      await tx.registration.create({
        data: {
          participantId: participant.id,
          rateId: rate.id,
          status: normalizeStatus(payload.Registration, "PENDING"),
          paymentStatus: normalizeStatus(payload.Payment, "PENDING"),
          submittedAt: new Date(),
          totalAmount: categoryAmount.toFixed(2),
          currency: rate.currency
        }
      });

      if (payload.QR === "Issued" || payload.Payment === "Paid") {
        await tx.qrCredential.create({
          data: {
            participantId: participant.id,
            tokenHash: tokenHash(`${participant.id}-${reference}-${Date.now()}`)
          }
        });
      }

      return tx.participantProfile.findUniqueOrThrow({
        where: { id: participant.id },
        include: {
          user: { select: { email: true } },
          chapter: { select: { id: true, name: true } },
          registrations: { orderBy: { submittedAt: "desc" }, take: 1 },
          qrCredentials: { where: { isActive: true }, orderBy: { issuedAt: "desc" }, take: 1 }
        }
      });
    });

    if ((payload.QR === "Issued" || payload.Payment === "Paid") && row.qrCredentials[0]) {
      await sendParticipantQrEmail({
        participant: row,
        qrSvg: await createQrSvg(row, row.qrCredentials[0])
      });
    }

    return ok(request, await toRow(row));
  } catch (error) {
    return fail(request, "PARTICIPANT_CREATE_FAILED", "Participant could not be added.", 500, error instanceof Error ? error.message : error);
  }
}

export async function PATCH(request: Request) {
  try {
    if (!await getAdminSessionFromRequest(request)) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    const payload = await request.json() as ParticipantPayload;
    if (!payload.id) return fail(request, "PARTICIPANT_ID_REQUIRED", "Participant id is required.");
    const participantId = payload.id;
    const { firstName, middleName, lastName } = participantNameParts(payload);
    if (!firstName.trim() || !lastName.trim()) return fail(request, "NAME_REQUIRED", "First name and last name are required.", 422);
    const category = payload.Category?.trim();
    const chapterId = category === "Chapter Delegate" ? payload.chapterId || null : null;

    let shouldSendQrEmail = false;
    const row = await prisma.$transaction(async (tx) => {
      const current = await tx.participantProfile.findUniqueOrThrow({
        where: { id: participantId },
        include: { user: true, registrations: { orderBy: { submittedAt: "desc" }, take: 1 } }
      });
      const nextCategory = category || current.participantCategory;
      const categoryAmount = await getParticipantCategoryAmount(nextCategory);
      const rate = await findOrCreateParticipantCategoryRate(nextCategory, categoryAmount);
      if (!rate) throw new Error("Create a registration period before updating participants.");

      await tx.user.update({
        where: { id: current.userId },
        data: { email: payload.Email?.trim().toLowerCase() || current.user.email }
      });

      await tx.participantProfile.update({
        where: { id: participantId },
        data: {
          registrationReference: payload.Reference?.trim() || current.registrationReference,
          firstName,
          middleName,
          lastName,
          mobileNumber: payload["Mobile Number"]?.trim() || null,
          company: payload.Company?.trim() || null,
          participantCategory: nextCategory,
          chapterId
        }
      });

      const registration = current.registrations[0];
      if (registration) {
        await tx.registration.update({
          where: { id: registration.id },
          data: {
            rateId: rate.id,
            status: normalizeStatus(payload.Registration, registration.status),
            paymentStatus: normalizeStatus(payload.Payment, registration.paymentStatus),
            totalAmount: categoryAmount.toFixed(2),
            currency: rate.currency
          }
        });
      }

      if (payload.QR === "Issued" || payload.Payment === "Paid") {
        const activeCredential = await tx.qrCredential.findFirst({ where: { participantId, isActive: true, revokedAt: null } });
        if (!activeCredential) {
          await tx.qrCredential.create({
            data: {
              participantId,
              tokenHash: tokenHash(`${participantId}-${payload.Reference ?? current.registrationReference}-${Date.now()}`)
            }
          });
          shouldSendQrEmail = true;
        }
      } else {
        await tx.qrCredential.updateMany({
          where: { participantId, isActive: true, revokedAt: null },
          data: { isActive: false, revokedAt: new Date(), revocationNote: "Updated from admin participant modal" }
        });
      }

      return tx.participantProfile.findUniqueOrThrow({
        where: { id: participantId },
        include: {
          user: { select: { email: true } },
          chapter: { select: { id: true, name: true } },
          registrations: { orderBy: { submittedAt: "desc" }, take: 1 },
          qrCredentials: { where: { isActive: true }, orderBy: { issuedAt: "desc" }, take: 1 }
        }
      });
    });

    if (shouldSendQrEmail && row.qrCredentials[0]) {
      await sendParticipantQrEmail({
        participant: row,
        qrSvg: await createQrSvg(row, row.qrCredentials[0])
      });
    }

    return ok(request, await toRow(row));
  } catch (error) {
    return fail(request, "PARTICIPANT_UPDATE_FAILED", "Participant could not be updated.", 500, error instanceof Error ? error.message : error);
  }
}
