import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { maskEmail, maskPhone, resolveMailSetting, resolveSmsSetting, sendMail, sendSms } from "@/lib/notify";
import {
  generateOtp,
  hashOtp,
  OTP_HOURLY_LIMIT,
  OTP_RESEND_COOLDOWN_SEC,
  OTP_TTL_MINUTES,
  resolveDestinations,
  verifyStepToken,
} from "@/lib/reset-otp";

const IS_DEV = process.env.NODE_ENV !== "production";

/**
 * Step 2 — send (or in dev, simulate) a 6-digit OTP to the chosen channel.
 * Credentials come from the institution's Control Panel when configured,
 * otherwise the central platform settings. Rate limits: 60s between sends,
 * max 6 OTPs per account per hour.
 */
export async function POST(req: Request) {
  const body = (await req.json().catch(() => ({}))) as { token?: string; channel?: string };
  const channel = body.channel === "SMS" ? "SMS" : body.channel === "EMAIL" ? "EMAIL" : "";
  const uid = await verifyStepToken(body.token ?? "", "identify");
  if (!uid || !channel) {
    return NextResponse.json({ error: "SESSION_EXPIRED" }, { status: 401 });
  }

  const user = await prisma.user.findUnique({
    where: { id: uid },
    include: {
      studentProfile: { select: { guardianPhone: true } },
      parentOfStudent: { select: { guardianPhone: true } },
      staff: { select: { phone: true } },
      institution: { select: { phone: true, name: true } },
    },
  });
  if (!user || user.status !== "ACTIVE") {
    return NextResponse.json({ error: "SESSION_EXPIRED" }, { status: 401 });
  }

  const dest = resolveDestinations(user);
  const target = channel === "EMAIL" ? dest.email : dest.phone;
  if (!target) return NextResponse.json({ error: "CHANNEL_UNAVAILABLE" }, { status: 400 });

  const now = Date.now();
  const recent = await prisma.passwordResetOtp.findFirst({
    where: { userId: uid, createdAt: { gt: new Date(now - OTP_RESEND_COOLDOWN_SEC * 1000) } },
    orderBy: { createdAt: "desc" },
  });
  if (recent) {
    return NextResponse.json(
      { error: "COOLDOWN", retryAfter: Math.ceil((recent.createdAt.getTime() + OTP_RESEND_COOLDOWN_SEC * 1000 - now) / 1000) },
      { status: 429 },
    );
  }
  const hourly = await prisma.passwordResetOtp.count({
    where: { userId: uid, createdAt: { gt: new Date(now - 3600_000) } },
  });
  if (hourly >= OTP_HOURLY_LIMIT) {
    return NextResponse.json({ error: "TOO_MANY" }, { status: 429 });
  }

  // resolve credentials: institution row first, central fallback after
  const code = generateOtp();
  const expiresAt = new Date(now + OTP_TTL_MINUTES * 60_000);
  const institutionName = user.institution?.name ?? "Increlog EMS";
  const message =
    channel === "EMAIL"
      ? `Your ${institutionName} password reset code is ${code}. It expires in ${OTP_TTL_MINUTES} minutes.`
      : `${institutionName}: your password reset code is ${code} (valid ${OTP_TTL_MINUTES} min).`;

  let deliveryError: "CHANNEL_NOT_CONFIGURED" | "SEND_FAILED" | null = null;
  if (channel === "EMAIL") {
    const resolved = await resolveMailSetting(user.institutionId);
    if (!resolved) {
      deliveryError = "CHANNEL_NOT_CONFIGURED";
    } else {
      try {
        await sendMail(resolved.setting, {
          to: target,
          subject: `${code} — password reset code`,
          text: message,
          html: `<div style="font-family:Poppins,Rubik,sans-serif;padding:24px;max-width:420px;margin:auto;border:1px solid #e6edef;border-radius:12px">
            <h2 style="color:#24695c;margin:0 0 4px">${institutionName}</h2>
            <p style="color:#525f7f;margin:0 0 16px">Password reset code</p>
            <div style="font-size:30px;font-weight:700;letter-spacing:8px;color:#24695c;background:rgba(36,105,92,0.07);border-radius:10px;padding:14px 0;text-align:center">${code}</div>
            <p style="color:#718096;font-size:13px;margin:16px 0 0">This code expires in ${OTP_TTL_MINUTES} minutes. If you didn't request it, you can ignore this message.</p>
          </div>`,
        });
      } catch (err) {
        console.error("otp-mail-failed", err);
        deliveryError = "SEND_FAILED";
      }
    }
  } else {
    const resolved = await resolveSmsSetting(user.institutionId);
    if (!resolved) {
      deliveryError = "CHANNEL_NOT_CONFIGURED";
    } else {
      try {
        await sendSms(resolved.setting, { phone: target, message });
      } catch (err) {
        console.error("otp-sms-failed", err);
        deliveryError = "SEND_FAILED";
      }
    }
  }

  if (deliveryError) {
    if (!IS_DEV) {
      return NextResponse.json({ error: deliveryError }, { status: deliveryError === "SEND_FAILED" ? 502 : 503 });
    }
    // dev/demo convenience: delivery unavailable — reveal the code so the
    // flow can be exercised end-to-end without SMTP/SMS credentials
    console.warn(`[dev-otp] ${deliveryError} — revealing code for demo. ${channel} → ${target}: ${code}`);
  }

  // invalidate any previous live codes, then store this one (hash only)
  await prisma.passwordResetOtp.updateMany({
    where: { userId: uid, consumedAt: null },
    data: { consumedAt: new Date() },
  });
  await prisma.passwordResetOtp.create({
    data: {
      userId: uid,
      channel,
      destination: channel === "EMAIL" ? maskEmail(target) : maskPhone(target),
      codeHash: hashOtp(code),
      expiresAt,
    },
  });

  return NextResponse.json({
    sent: true,
    masked: channel === "EMAIL" ? maskEmail(target) : maskPhone(target),
    expiresAt: expiresAt.toISOString(),
    ...(IS_DEV ? { devCode: code } : {}),
  });
}
