import { NextResponse } from "next/server";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";
import { resolveMailSetting, sendMail } from "@/lib/notify";

/* Public admission application (the /apply page). Anyone can apply to an
   ACTIVE institution by its code — the application lands in the portal
   Admissions queue with applicationType "PUBLIC".

   Abuse guards: a honeypot field (silently accepted, never stored) and a
   light in-memory per-IP rate limit. */

const WINDOW_MS = 60 * 60 * 1000; // 1 hour
const MAX_PER_WINDOW = 8;
const submissions = new Map<string, number[]>();

function rateLimited(ip: string): boolean {
  const now = Date.now();
  const recent = (submissions.get(ip) ?? []).filter((ts) => now - ts < WINDOW_MS);
  if (recent.length >= MAX_PER_WINDOW) {
    submissions.set(ip, recent);
    return true;
  }
  recent.push(now);
  submissions.set(ip, recent);
  // keep the map small
  if (submissions.size > 1000) {
    for (const [key, list] of submissions) {
      if (list.every((ts) => now - ts >= WINDOW_MS)) submissions.delete(key);
    }
  }
  return false;
}

function clientIp(req: Request): string {
  const fwd = req.headers.get("x-forwarded-for");
  return (fwd ? fwd.split(",")[0] : null) ?? "local";
}

export async function POST(req: Request) {
  try {
    const body = (await req.json().catch(() => ({}))) as Record<string, unknown>;

    // honeypot: bots fill every field — humans never see this one
    if (String(body.company ?? "").trim()) {
      return NextResponse.json({ success: true });
    }

    if (rateLimited(clientIp(req))) {
      return NextResponse.json({ error: "RATE_LIMITED" }, { status: 429 });
    }

    const code = String(body.code ?? "").trim().toUpperCase();
    const studentName = String(body.studentName ?? "").trim();
    const guardianPhone = String(body.guardianPhone ?? "").trim();
    if (!code || !studentName || !guardianPhone) {
      return NextResponse.json({ error: "MISSING_FIELDS" }, { status: 400 });
    }

    const institution = await prisma.institution.findUnique({
      where: { code },
      select: { id: true, name: true, code: true, status: true, admissionFeeEnabled: true, admissionFeeAmount: true },
    });
    if (!institution || institution.status !== "ACTIVE") {
      return NextResponse.json({ error: "INSTITUTION_NOT_FOUND" }, { status: 404 });
    }

    // snapshot the Control Panel admission-fee setting onto this application
    const feeAmount =
      institution.admissionFeeEnabled && Number(institution.admissionFeeAmount) > 0
        ? Number(institution.admissionFeeAmount)
        : 0;

    // class must belong to this institution's selections (if provided)
    let classItemId: number | null = null;
    if (body.classItemId) {
      const classItem = await prisma.academicSelection.findUnique({
        where: {
          institutionId_academicItemId: {
            institutionId: institution.id,
            academicItemId: Number(body.classItemId),
          },
        },
        select: { academicItemId: true },
      });
      classItemId = classItem?.academicItemId ?? null;
    }

    // the applied-for session, if provided, must equally be one of the
    // institution's selected academic items
    let sessionItemId: number | null = null;
    if (body.sessionItemId) {
      const sessionItem = await prisma.academicSelection.findUnique({
        where: {
          institutionId_academicItemId: {
            institutionId: institution.id,
            academicItemId: Number(body.sessionItemId),
          },
        },
        select: { academicItem: { select: { category: true } } },
      });
      sessionItemId =
        sessionItem?.academicItem.category === "SESSION" ? Number(body.sessionItemId) : null;
    }

    // rapid applications carry no guardian name — default it to the student name
    const guardianName = String(body.guardianName ?? "").trim() || studentName;
    const note = String(body.note ?? "").trim() || null;
    const email = String(body.email ?? "").trim() || null;
    const address = String(body.address ?? "").trim() || null;

    // optional minimal tracking account (Name/email/contact/address/password
    // + this institution) — used to verify identity on the public /track page
    const accountPassword = String(body.password ?? "");
    let applicantId: number | null = null;
    if (accountPassword.length >= 6 && email) {
      const existing = await prisma.applicant.findUnique({
        where: { institutionId_email: { institutionId: institution.id, email } },
      });
      if (existing && (await bcrypt.compare(accountPassword, existing.passwordHash))) {
        applicantId = existing.id;
      } else if (!existing) {
        const applicant = await prisma.applicant.create({
          data: {
            institutionId: institution.id,
            name: guardianName || studentName,
            email,
            phone: guardianPhone,
            address,
            passwordHash: await bcrypt.hash(accountPassword, 10),
          },
        });
        applicantId = applicant.id;
      }
    }

    const admission = await prisma.$transaction(async (tx) => {
      const created = await tx.admission.create({
        data: {
          institutionId: institution.id,
          studentName,
          applicationType: "PUBLIC",
          studentNameBn: String(body.studentNameBn ?? "").trim() || null,
          gender: ["MALE", "FEMALE", "OTHER"].includes(String(body.gender)) ? String(body.gender) : "MALE",
          dob: body.dob ? new Date(String(body.dob)) : null,
          guardianName,
          guardianNameBn: String(body.guardianNameBn ?? "").trim() || null,
          guardianPhone,
          motherName: String(body.motherName ?? "").trim() || null,
          motherNameBn: String(body.motherNameBn ?? "").trim() || null,
          email,
          address,
          permanentAddress: String(body.permanentAddress ?? "").trim() || null,
          studentPhone: String(body.studentPhone ?? "").trim() || null,
          bloodGroup: String(body.bloodGroup ?? "").trim() || null,
          religion: String(body.religion ?? "").trim() || null,
          classItemId,
          sessionItemId,
          note,
          feeAmount,
          feeStatus: feeAmount > 0 ? "DUE" : "NONE",
          applicantId,
        },
      });
      // an application note becomes the first (applicant) message in the thread
      if (note) {
        await tx.message.create({
          data: {
            institutionId: institution.id,
            admissionId: created.id,
            sender: "APPLICANT",
            senderName: guardianName,
            body: note,
          },
        });
      }
      return created;
    });

    const reference = `APP-${String(admission.id).padStart(5, "0")}`;

    // email the applicant their reference + tracking link through the
    // institution's Control-Panel mail settings (central fallback)
    let emailSent = false;
    if (email) {
      try {
        const mail = await resolveMailSetting(institution.id);
        if (mail) {
          const origin = new URL(req.url).origin;
          const trackUrl = `${origin}/track?code=${institution.code}&ref=${reference}`;
          await sendMail(mail.setting, {
            to: email,
            subject: `Admission application received — ${reference} · ${institution.name}`,
            text:
              `Dear ${guardianName},\n\n` +
              `Your admission application to ${institution.name} has been received.\n` +
              `Application reference: ${reference}\n` +
              `Student: ${studentName}\n` +
              (feeAmount > 0 ? `Application fee due: BDT ${feeAmount}\n` : "") +
              `\nTrack your application status anytime:\n${trackUrl}\n\n` +
              `— ${institution.name} (via IncrelogEMS)`,
            html:
              `<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#242934;line-height:1.6">` +
              `<p>Dear <b>${guardianName}</b>,</p>` +
              `<p>Your admission application to <b>${institution.name}</b> has been received.</p>` +
              `<p>Application reference: <b style="font-size:16px;color:#24695c">${reference}</b><br/>Student: ${studentName}</p>` +
              (feeAmount > 0 ? `<p>Application fee due: <b>BDT ${feeAmount}</b></p>` : "") +
              `<p><a href="${trackUrl}" style="display:inline-block;background:#24695c;color:#fff;padding:9px 18px;border-radius:8px;text-decoration:none">Track your application</a></p>` +
              `<p style="color:#8a97a8;font-size:12px">— ${institution.name} (via IncrelogEMS)</p></div>`,
          });
          emailSent = true;
        }
      } catch {
        // notification failures must never fail the application itself
      }
    }

    return NextResponse.json({
      success: true,
      reference,
      institutionName: institution.name,
      feeDue: feeAmount,
      emailSent,
    });
  } catch {
    return NextResponse.json({ error: "Could not submit application" }, { status: 500 });
  }
}
