import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireAnyRole } from "@/lib/auth";
import { getMemberStudentId } from "@/lib/member";
import { PAYMENT_METHODS } from "@/lib/constants";

/** Online payment claims of the caller's own (child's) fees. The student/
    parent pays an institution collection account (bKash/bank/…) and submits
    the transaction reference; the claim stays PENDING until the institution
    verifies it and posts the fee collection (invoice). */

const MOBILE = ["BKASH", "NAGAD", "ROCKET"];

function requiredDetailFields(method: string): string[] {
  if (MOBILE.includes(method)) return ["txnId"];
  if (method === "BANK_TRANSFER") return ["bankName", "refNo"];
  if (method === "CARD") return ["refNo"];
  return [];
}

export async function GET() {
  const session = await requireAnyRole(["STUDENT", "PARENT"]);
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const studentId = await getMemberStudentId(session.userId);
  if (!studentId) {
    return NextResponse.json({ error: "NO_LINKED_STUDENT" }, { status: 409 });
  }

  const claims = await prisma.feePaymentClaim.findMany({
    where: { institutionId: session.institutionId, studentId },
    orderBy: { createdAt: "desc" },
    take: 100,
    select: {
      id: true,
      amount: true,
      method: true,
      accountLabel: true,
      details: true,
      note: true,
      status: true,
      decisionNote: true,
      decidedAt: true,
      createdAt: true,
      feeSchedule: { select: { title: true, month: true } },
      collection: { select: { invoiceNo: true } },
    },
  });

  return NextResponse.json({
    claims: claims.map((c) => ({
      id: c.id,
      amount: Number(c.amount),
      method: c.method,
      accountLabel: c.accountLabel,
      details: c.details ?? null,
      note: c.note,
      status: c.status,
      decisionNote: c.decisionNote,
      decidedAt: c.decidedAt,
      createdAt: c.createdAt,
      feeTitle: c.feeSchedule?.title ?? null,
      month: c.feeSchedule?.month ?? null,
      invoiceNo: c.collection?.invoiceNo ?? null,
    })),
  });
}

export async function POST(req: Request) {
  const session = await requireAnyRole(["STUDENT", "PARENT"]);
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const studentId = await getMemberStudentId(session.userId);
  if (!studentId) {
    return NextResponse.json({ error: "NO_LINKED_STUDENT" }, { status: 409 });
  }

  try {
    const body = await req.json();
    const feeScheduleId = body.feeScheduleId ? Number(body.feeScheduleId) : null;
    const amount = Number(body.amount);
    const method = String(body.method ?? "");
    const accountLabel = String(body.accountLabel ?? "").trim().slice(0, 80) || null;
    const note = String(body.note ?? "").trim().slice(0, 300) || null;

    if (!PAYMENT_METHODS.includes(method as never) || method === "CASH") {
      return NextResponse.json({ error: "INVALID_METHOD" }, { status: 400 });
    }
    if (!Number.isFinite(amount) || amount <= 0) {
      return NextResponse.json({ error: "INVALID_AMOUNT" }, { status: 400 });
    }

    // method-specific transaction reference (same shape the office records)
    const details: Record<string, string> = {};
    if (body.details && typeof body.details === "object") {
      for (const [key, value] of Object.entries(body.details as Record<string, unknown>)) {
        const k = key.replace(/[^a-zA-Z0-9]/g, "").slice(0, 20);
        const v = String(value ?? "").trim().slice(0, 60);
        if (k && v) details[k] = v;
      }
    }
    const missing = requiredDetailFields(method).filter((k) => !(details[k] ?? "").trim());
    if (missing.length > 0) {
      return NextResponse.json({ error: "DETAILS_REQUIRED", missing }, { status: 400 });
    }

    const student = await prisma.student.findFirst({
      where: { id: studentId, institutionId: session.institutionId },
      select: { id: true },
    });
    if (!student) return NextResponse.json({ error: "NO_LINKED_STUDENT" }, { status: 409 });

    let schedule = null;
    if (feeScheduleId) {
      schedule = await prisma.feeSchedule.findFirst({
        where: { id: feeScheduleId, institutionId: session.institutionId, studentId },
        include: { payments: { select: { amount: true } } },
      });
      if (!schedule) return NextResponse.json({ error: "Schedule not found" }, { status: 404 });
      const paid = schedule.payments.reduce((s, p) => s + Number(p.amount), 0);
      if (amount > Number(schedule.amount) - paid) {
        return NextResponse.json({ error: "AMOUNT_EXCEEDS" }, { status: 400 });
      }
      // one open claim per fee item — duplicates would double-post on approval
      const open = await prisma.feePaymentClaim.findFirst({
        where: { institutionId: session.institutionId, studentId, feeScheduleId, status: "PENDING" },
        select: { id: true },
      });
      if (open) return NextResponse.json({ error: "CLAIM_EXISTS" }, { status: 409 });
    }

    const claim = await prisma.feePaymentClaim.create({
      data: {
        institutionId: session.institutionId!,
        studentId,
        feeScheduleId,
        amount,
        method: method as never,
        accountLabel,
        details: Object.keys(details).length > 0 ? details : undefined,
        note,
        status: "PENDING",
      },
      select: { id: true, status: true, createdAt: true },
    });

    return NextResponse.json({ claim });
  } catch {
    return NextResponse.json({ error: "Could not submit payment claim" }, { status: 500 });
  }
}
