import { NextResponse } from "next/server";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";
import { PAYMENT_METHODS } from "@/lib/constants";

/* Public application tracking (the /track page, enabled per institution from
   the Control Panel). The applicant proves identity with the reference no +
   either the phone/email on the application or the password of the minimal
   tracking account created while applying. Verified applicants may also
   submit their fee payment transaction (gateway + TrxID) for the institution
   to verify. */

/** "APP-00030" | "app30" | "30" → application id */
function parseReference(raw: string): number | null {
  const m = /(\d+)\s*$/.exec(raw.trim());
  if (!m) return null;
  const id = Number(m[1]);
  return Number.isInteger(id) && id > 0 ? id : null;
}

const digits = (v: string | null | undefined) => (v ?? "").replace(/\D/g, "");

/** does the supplied contact match the application's phone/email on file? */
function contactMatches(admission: { email: string | null; guardianPhone: string; studentPhone: string | null }, contact: string) {
  const c = contact.trim().toLowerCase();
  if (!c) return false;
  if (admission.email && admission.email.trim().toLowerCase() === c) return true;
  const cd = digits(c);
  if (!cd) return false;
  return (
    (cd.length >= 6 && digits(admission.guardianPhone).endsWith(cd.slice(-10))) ||
    (admission.studentPhone && cd.length >= 6 && digits(admission.studentPhone).endsWith(cd.slice(-10))) ||
    digits(admission.guardianPhone) === cd ||
    digits(admission.studentPhone ?? "") === cd
  );
}

async function resolveTracked(code: string, reference: string) {
  const institution = await prisma.institution.findUnique({
    where: { code: code.trim().toUpperCase() },
    select: {
      id: true,
      name: true,
      code: true,
      status: true,
      trackingEnabled: true,
      collectionAccounts: {
        where: { isActive: true },
        select: {
          type: true, provider: true, accountType: true, label: true,
          accountName: true, accountNumber: true, branch: true, note: true,
        },
        orderBy: [{ sortOrder: "asc" }, { id: "asc" }],
      },
    },
  });
  if (!institution || institution.status !== "ACTIVE") {
    return { error: "INSTITUTION_NOT_FOUND" as const };
  }
  if (!institution.trackingEnabled) {
    return { error: "TRACKING_DISABLED" as const };
  }
  const id = parseReference(reference);
  if (!id) return { error: "BAD_REFERENCE" as const };

  const admission = await prisma.admission.findFirst({
    where: { id, institutionId: institution.id },
    include: {
      classItem: { select: { name: true, nameBn: true } },
      sessionItem: { select: { name: true, nameBn: true } },
      student: { select: { roll: true } },
      applicant: { select: { id: true, email: true } },
    },
  });
  if (!admission) return { error: "NOT_FOUND" as const };
  return { institution, admission };
}

/** identity check: applicant-account password, or the phone/email on file */
async function verifyIdentity(
  admission: { email: string | null; guardianPhone: string; studentPhone: string | null; applicantId: number | null },
  verify: { contact?: string; password?: string },
): Promise<boolean> {
  if (verify.password && admission.applicantId) {
    const applicant = await prisma.applicant.findUnique({ where: { id: admission.applicantId } });
    if (applicant && (await bcrypt.compare(verify.password, applicant.passwordHash))) return true;
  }
  if (verify.contact && contactMatches(admission, verify.contact)) return true;
  return false;
}

/** queue position among this institution's pending applications (oldest first) */
function queuePosition(
  admission: NonNullable<Awaited<ReturnType<typeof resolveTracked>>["admission"]>,
  institutionId: number
) {
  return admission.status === "PENDING"
    ? prisma.admission.count({
        where: { institutionId, status: "PENDING", createdAt: { lte: admission.createdAt } },
      })
    : null;
}

export async function POST(req: Request) {
  try {
    const body = (await req.json().catch(() => ({}))) as {
      action?: string;
      code?: string;
      reference?: string;
      verify?: { contact?: string; password?: string };
      method?: string;
      txnId?: string;
      account?: string;
    };

    const resolved = await resolveTracked(String(body.code ?? ""), String(body.reference ?? ""));
    if ("error" in resolved) {
      return NextResponse.json({ error: resolved.error }, { status: resolved.error === "INSTITUTION_NOT_FOUND" ? 404 : 400 });
    }
    const { institution, admission } = resolved;

    if (!(await verifyIdentity(admission, body.verify ?? {}))) {
      return NextResponse.json({ error: "WRONG_VERIFY" }, { status: 403 });
    }

    /* ---- applicant submits their fee payment transaction for verification ---- */
    if (body.action === "submit-payment") {
      if (Number(admission.feeAmount) <= 0) {
        return NextResponse.json({ error: "NO_FEE" }, { status: 400 });
      }
      if (admission.feeStatus !== "DUE") {
        return NextResponse.json({ error: "NOT_DUE" }, { status: 409 });
      }
      const method = String(body.method ?? "");
      const txnId = String(body.txnId ?? "").trim().slice(0, 60);
      if (!PAYMENT_METHODS.includes(method as never) || method === "CASH") {
        return NextResponse.json({ error: "INVALID_METHOD" }, { status: 400 });
      }
      if (!txnId) return NextResponse.json({ error: "MISSING_TXN" }, { status: 400 });

      await prisma.admission.update({
        where: { id: admission.id },
        data: {
          feeMethod: method as never,
          feeDetails: {
            txnId,
            account: String(body.account ?? "").trim().slice(0, 60) || undefined,
            submittedByApplicant: true,
            submittedAt: new Date().toISOString(),
          },
          // stays DUE — the institution verifies the transaction, then marks paid
        },
      });
      return NextResponse.json({ success: true });
    }

    /* ---- plain status lookup ---- */
    const [position, totalPending] = await Promise.all([
      queuePosition(admission, institution.id),
      prisma.admission.count({ where: { institutionId: institution.id, status: "PENDING" } }),
    ]);

    return NextResponse.json({
      application: {
        reference: `APP-${String(admission.id).padStart(5, "0")}`,
        studentName: admission.studentName,
        className: admission.classItem?.name ?? null,
        sessionName: admission.sessionItem?.name ?? null,
        status: admission.status,
        createdAt: admission.createdAt,
        decidedAt: admission.decidedAt,
        roll: admission.student?.roll ?? null,
        position,
        totalPending,
        fee: {
          amount: Number(admission.feeAmount),
          status: admission.feeStatus,
          method: admission.feeMethod,
          txnId: (admission.feeDetails as Record<string, string> | null)?.txnId ?? null,
          submittedByApplicant: !!(admission.feeDetails as Record<string, unknown> | null)?.submittedByApplicant,
        },
      },
      institution: { name: institution.name, code: institution.code },
      paymentAccounts: institution.collectionAccounts,
    });
  } catch {
    return NextResponse.json({ error: "Could not track application" }, { status: 500 });
  }
}
