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, getParticipantCategoryOptions, saveParticipantCategoryOptions } from "../_categories";
import { fallbackParticipantDefaultPassword, getPaidCompedParticipantDefaultPassword, shouldUsePaidCompedDefaultPassword } from "../_default-password";
import { sendParticipantQrEmail } from "../_mailer";
import { createQrSvg } from "../_qr";
import { generateParticipantReference } from "../_reference";

type ImportRow = {
  Name: string;
  Email: string;
  Company: string;
  Category: string;
  Amount: number;
  Registration: string;
  Payment: string;
  QR: string;
  Chapter: string;
};

function splitCsvLine(line: string) {
  const values: string[] = [];
  let current = "";
  let inQuotes = false;
  for (let index = 0; index < line.length; index += 1) {
    const char = line[index];
    const next = line[index + 1];
    if (char === "\"" && inQuotes && next === "\"") {
      current += "\"";
      index += 1;
    } else if (char === "\"") {
      inQuotes = !inQuotes;
    } else if (char === "," && !inQuotes) {
      values.push(current.trim());
      current = "";
    } else {
      current += char;
    }
  }
  values.push(current.trim());
  return values;
}

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 splitName(name: string) {
  const parts = name.trim().split(/\s+/).filter(Boolean);
  if (parts.length <= 1) return { firstName: parts[0] ?? "", lastName: "Participant" };
  return { firstName: parts[0] ?? "", lastName: parts.slice(1).join(" ") };
}

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

function parseAmount(value: string) {
  const amount = Number(value.replace(/[^0-9.-]/g, ""));
  return Number.isFinite(amount) && amount >= 0 ? Number(amount.toFixed(2)) : 0;
}

function parseRows(csv: string) {
  const lines = csv.replace(/^\uFEFF/, "").split(/\r?\n/).filter((line) => line.trim());
  if (lines.length < 2) return [];
  const headers = splitCsvLine(lines[0] ?? "").map((header) => header.trim());
  return lines.slice(1).map((line) => {
    const values = splitCsvLine(line);
    const record = Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
    return {
      Name: String(record.Name ?? "").trim(),
      Email: String(record.Email ?? "").trim().toLowerCase(),
      Company: String(record.Company ?? "").trim(),
      Category: String(record.Category ?? "Guest Only").trim() || "Guest Only",
      Amount: parseAmount(String(record.Amount ?? "0")),
      Registration: String(record.Registration ?? "Pending").trim() || "Pending",
      Payment: String(record.Payment ?? "Pending").trim() || "Pending",
      QR: String(record.QR ?? "").trim(),
      Chapter: String(record.Chapter ?? "").trim()
    };
  });
}

async function csvFromRequest(request: Request) {
  const contentType = request.headers.get("content-type") ?? "";
  if (contentType.includes("multipart/form-data")) {
    const formData = await request.formData();
    const file = formData.get("file");
    if (file instanceof File) return file.text();
    const csv = formData.get("csv");
    return typeof csv === "string" ? csv : "";
  }
  const body = await request.json().catch(() => null) as { csv?: unknown } | null;
  return typeof body?.csv === "string" ? body.csv : "";
}

async function chapterIdForName(name: string) {
  if (!name) return null;
  const chapter = await prisma.chapter.upsert({
    where: { name },
    update: {},
    create: { name }
  });
  return chapter.id;
}

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

export async function POST(request: Request) {
  try {
    if (!await getAdminSessionFromRequest(request)) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    const csv = await csvFromRequest(request);
    if (!csv.trim()) return fail(request, "IMPORT_FILE_REQUIRED", "Upload a CSV file to import participants.", 422);

    const rows = parseRows(csv);
    const validRows = rows.filter((row) => row.Name && row.Email);
    if (validRows.length === 0) return fail(request, "IMPORT_ROWS_REQUIRED", "The CSV needs at least one row with Name and Email.", 422);

    const categoryAmounts = new Map<string, number>();
    validRows.forEach((row) => categoryAmounts.set(row.Category, row.Amount));
    const existingCategories = await getParticipantCategoryOptions();
    const mergedCategories = [
      ...existingCategories,
      ...Array.from(categoryAmounts.entries()).map(([name, amount]) => ({ name, amount }))
    ].map((category) => ({ ...category, amount: categoryAmounts.get(category.name) ?? category.amount }));
    await saveParticipantCategoryOptions(mergedCategories);
    const paidCompedDefaultPassword = await getPaidCompedParticipantDefaultPassword();

    const importedRows = [];
    let qrIssued = 0;
    for (const importRow of validRows) {
      const { firstName, lastName } = splitName(importRow.Name);
      const chapterId = importRow.Category === "Chapter Delegate" ? await chapterIdForName(importRow.Chapter) : null;
      const rate = await findOrCreateParticipantCategoryRate(importRow.Category, importRow.Amount);
      if (!rate) return fail(request, "REGISTRATION_RATE_REQUIRED", "Create a registration period before importing participants.", 409);
      const shouldIssueQr = importRow.Payment === "Paid" || importRow.QR === "Issued";
      const defaultPassword = shouldUsePaidCompedDefaultPassword(importRow.Payment) ? paidCompedDefaultPassword : fallbackParticipantDefaultPassword();
      let createdCredential = false;

      const participant = await prisma.$transaction(async (tx) => {
        const user = await tx.user.upsert({
          where: { email: importRow.Email },
          update: { emailVerifiedAt: new Date() },
          create: {
            email: importRow.Email,
            passwordHash: await hashPassword(defaultPassword),
            emailVerifiedAt: new Date()
          }
        });

        const existingParticipant = await tx.participantProfile.findUnique({ where: { userId: user.id } });
        const participantRecord = existingParticipant
          ? await tx.participantProfile.update({
            where: { id: existingParticipant.id },
            data: {
              firstName,
              lastName,
              company: importRow.Company || null,
              participantCategory: importRow.Category,
              chapterId
            }
          })
          : await tx.participantProfile.create({
            data: {
              userId: user.id,
              registrationReference: await generateParticipantReference(),
              firstName,
              lastName,
              company: importRow.Company || null,
              participantCategory: importRow.Category,
              chapterId,
              verificationStatus: "PENDING"
            }
          });

        const registration = await tx.registration.findFirst({
          where: { participantId: participantRecord.id },
          orderBy: { submittedAt: "desc" }
        });
        if (registration) {
          await tx.registration.update({
            where: { id: registration.id },
            data: {
              rateId: rate.id,
              status: normalizeStatus(importRow.Registration, registration.status),
              paymentStatus: normalizeStatus(importRow.Payment, registration.paymentStatus),
              totalAmount: importRow.Amount.toFixed(2),
              currency: rate.currency
            }
          });
        } else {
          await tx.registration.create({
            data: {
              participantId: participantRecord.id,
              rateId: rate.id,
              status: normalizeStatus(importRow.Registration, "PENDING"),
              paymentStatus: normalizeStatus(importRow.Payment, "PENDING"),
              submittedAt: new Date(),
              totalAmount: importRow.Amount.toFixed(2),
              currency: rate.currency
            }
          });
        }

        if (shouldIssueQr) {
          const activeCredential = await tx.qrCredential.findFirst({
            where: { participantId: participantRecord.id, isActive: true, revokedAt: null }
          });
          if (!activeCredential) {
            await tx.qrCredential.create({
              data: {
                participantId: participantRecord.id,
                tokenHash: tokenHash(`${participantRecord.id}-${participantRecord.registrationReference}-${Date.now()}`)
              }
            });
            createdCredential = true;
          }
        }

        return tx.participantProfile.findUniqueOrThrow({
          where: { id: participantRecord.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 (shouldIssueQr && createdCredential && participant.qrCredentials[0]) {
        qrIssued += 1;
        await sendParticipantQrEmail({
          participant,
          qrSvg: await createQrSvg(participant, participant.qrCredentials[0])
        });
      }
      importedRows.push(rowResponse(participant));
    }

    return ok(request, {
      imported: importedRows.length,
      skipped: rows.length - validRows.length,
      qrIssued,
      rows: importedRows
    });
  } catch (error) {
    return fail(request, "PARTICIPANT_IMPORT_FAILED", "Participants could not be imported.", 500, error instanceof Error ? error.message : error);
  }
}
