/**
 * POST /api/billing/change-plan
 * Execute subscription plan change (upgrade or downgrade)
 *
 * Supports the 5-tier system:
 * - tier1 (Starter)
 * - tier2 (Professional)
 * - tier3 (Business)
 * - tier4 (Enterprise)
 * - tier5 (Ultimate)
 *
 * Security:
 * - CSRF protection
 * - Authentication required
 * - Stripe webhook will confirm the change
 */

import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { withCSRF } from "@/lib/csrf";
import { prisma } from "@/lib/db";
import { logger } from "@/lib/logger";
import { getAuthenticatedEmail } from "@/lib/utils/auth-helpers.util";
import { upgradeSubscription } from "@/lib/stripe";
import { PRICING_TIERS } from "@/lib/config/pricing-tiers";
import {
  type TierId,
  type BillingPeriod,
  type PlanChangeExecuteRequest,
  type PlanChangeExecuteResponse,
  isValidTierId,
  isValidBillingPeriod,
  TIER_ORDER,
} from "@/lib/types/plan-change.types";
import { CURRENCY_RATES } from "@/lib/utils/pricing-helpers";
import {
  parseStripeError,
  createErrorResponse,
} from "@/lib/errors/plan-change-errors";
import { convexMutation } from "@/lib/convex-client";
async function handleChangePlan(request: NextRequest) {
  try {
    // Authenticate using either NextAuth or OTP token
    const userEmail = await getAuthenticatedEmail(request, auth);

    if (!userEmail) {
      logger.warn("Unauthorized plan change request");
      return NextResponse.json(
        { error: "Unauthorized", code: "UNAUTHORIZED" },
        { status: 401 },
      );
    }

    const body: PlanChangeExecuteRequest = await request.json();
    const {
      newTierId,
      billingPeriod: requestedBillingPeriod,
      confirmedAmount,
    } = body;

    // Validate tier ID
    if (!newTierId || !isValidTierId(newTierId)) {
      return NextResponse.json(
        { error: "Invalid plan type", code: "INVALID_TIER" },
        { status: 400 },
      );
    }

    // Find user's active subscription
    const subscription = await prisma.subscriptions.findFirst({
      where: {
        users: {
          email: userEmail,
        },
        status: {
          in: ["active", "trialing", "past_due"],
        },
      },
      include: {
        users: true,
      },
    });

    if (!subscription) {
      return NextResponse.json(
        {
          error: "No active subscription found",
          code: "NO_ACTIVE_SUBSCRIPTION",
        },
        { status: 404 },
      );
    }

    const currentTierId = subscription.tierId as TierId;

    // Validate current tier
    if (!isValidTierId(currentTierId)) {
      logger.error("Invalid current tier in subscription", {
        subscriptionId: subscription.id,
        tierId: currentTierId,
      });
      return NextResponse.json(
        { error: "Invalid subscription configuration", code: "INVALID_TIER" },
        { status: 500 },
      );
    }

    // Check if trying to switch to same plan
    if (currentTierId === newTierId) {
      return NextResponse.json(
        { error: "Already on this plan", code: "SAME_PLAN" },
        { status: 400 },
      );
    }

    // Use provided billing period or keep current
    const billingPeriod: BillingPeriod =
      requestedBillingPeriod && isValidBillingPeriod(requestedBillingPeriod)
        ? requestedBillingPeriod
        : (subscription.billingPeriod as BillingPeriod);

    // Determine if upgrade or downgrade
    const isUpgrade = TIER_ORDER[newTierId] > TIER_ORDER[currentTierId];

    // Get tier details for response
    const newTier = PRICING_TIERS.find((t) => t.id === newTierId);
    if (!newTier) {
      return NextResponse.json(
        { error: "Tier configuration not found", code: "INVALID_TIER" },
        { status: 500 },
      );
    }

    // Validate confirmedAmount to prevent TOCTOU attacks
    // Re-calculate expected amount server-side to compare with client-confirmed amount
    if (confirmedAmount !== undefined) {
      const currentTier = PRICING_TIERS.find((t) => t.id === currentTierId);
      if (currentTier && newTier) {
        // Calculate expected proration using same logic as preview API
        const QAR_RATE = CURRENCY_RATES.QAR;
        const currentPriceQAR = currentTier.priceGBP * QAR_RATE;
        const newPriceQAR = newTier.priceGBP * QAR_RATE;

        // Apply yearly discount if applicable
        // For yearly: apply 7% discount (0.93) and multiply by 12 months
        const yearlyMultiplier = billingPeriod === "yearly" ? 12 * 0.93 : 1;
        const currentMonthlyInSmallestUnit = Math.round(
          currentPriceQAR * yearlyMultiplier * 100,
        );
        const newMonthlyInSmallestUnit = Math.round(
          newPriceQAR * yearlyMultiplier * 100,
        );

        const totalDaysInPeriod = billingPeriod === "yearly" ? 365 : 30;
        const now = new Date();
        const endDate = new Date(subscription.currentPeriodEnd);
        const daysRemaining = Math.max(
          0,
          Math.ceil(
            (endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),
          ),
        );

        const currentDailyRate = Math.round(
          currentMonthlyInSmallestUnit / totalDaysInPeriod,
        );
        const newDailyRate = Math.round(
          newMonthlyInSmallestUnit / totalDaysInPeriod,
        );
        const creditFromCurrent = currentDailyRate * daysRemaining;
        const chargeForNew = newDailyRate * daysRemaining;
        const expectedNetAmount = chargeForNew - creditFromCurrent;

        // Allow 5% tolerance for timing differences between preview and execute
        const tolerance = Math.abs(expectedNetAmount * 0.05) + 100; // 5% + 1 QAR buffer
        if (Math.abs(confirmedAmount - expectedNetAmount) > tolerance) {
          logger.warn("Amount mismatch in plan change", {
            confirmedAmount,
            expectedNetAmount,
            tolerance,
            userId: subscription.userId,
          });
          return NextResponse.json(
            {
              error:
                "Amount has changed since preview. Please refresh and try again.",
              code: "AMOUNT_MISMATCH",
              details: {
                expected: expectedNetAmount,
                confirmed: confirmedAmount,
              },
            },
            { status: 400 },
          );
        }
      }
    }

    // Determine if user is in trial
    const isInTrial = subscription.status === "trialing";

    // Convex: mirror subscription tier update to Convex before Stripe call
    try {
      await convexMutation("billing/mutations:updateSubscription", {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        id: subscription.id as any,
      });
    } catch (convexErr) {
      // Non-fatal: Stripe webhook will sync the subscription state
      logger.warn("Convex billing update failed (non-fatal)", {
        error:
          convexErr instanceof Error ? convexErr.message : String(convexErr),
      });
    }

    // Handle upgrade (downgrades are blocked above)
    // Note: No setup fee charged - user already paid at signup
    // Note: No trial period - user already had their trial (resetTrial: false)
    const result = await upgradeSubscription({
      subscriptionId: subscription.id,
      newTierId,
      currency: "gbp", // Base currency for Stripe
      billingPeriod,
      resetTrial: false, // Never reset trial on plan changes
      createImmediateInvoice: !isInTrial, // Only create immediate invoice if not in trial
    });
    const prorationAmount = result.prorationAmount;

    // Atomic dual database audit logging
    try {
    } catch (auditError) {
      // Log but don't fail the request if audit logging fails
      logger.error("Failed to save audit log", {
        error:
          auditError instanceof Error ? auditError.message : "Unknown error",
      });
    }

    logger.info("Plan change executed successfully", {
      userId: subscription.userId,
      fromTier: currentTierId,
      toTier: newTierId,
      isUpgrade,
      prorationAmount,
    });

    const response: PlanChangeExecuteResponse = {
      success: true,
      subscription: {
        id: subscription.id,
        tierId: newTierId,
        status: subscription.status, // Status doesn't change immediately
        currentPeriodEnd: subscription.currentPeriodEnd.toISOString(),
      },
      isUpgrade,
      message: {
        en: isUpgrade
          ? `Successfully upgraded to ${newTier.nameEn}!${isInTrial ? " Changes will take effect after your trial ends." : ""}`
          : `Successfully downgraded to ${newTier.nameEn}.${isInTrial ? " Changes will take effect after your trial ends." : ""}`,
        ar: isUpgrade
          ? `تمت الترقية بنجاح إلى ${newTier.nameAr}!${isInTrial ? " ستدخل التغييرات حيز التنفيذ بعد انتهاء الفترة التجريبية." : ""}`
          : `تم التخفيض بنجاح إلى ${newTier.nameAr}.${isInTrial ? " ستدخل التغييرات حيز التنفيذ بعد انتهاء الفترة التجريبية." : ""}`,
      },
    };

    // Include invoice info if available
    if ("invoiceId" in result && result.invoiceId) {
      response.invoice = {
        id: result.invoiceId,
        amount: prorationAmount,
        status: "pending",
      };
    }

    return NextResponse.json(response);
  } catch (error: unknown) {
    const parsedError = parseStripeError(error);

    logger.error("Failed to change plan", {
      error: error instanceof Error ? error.message : "Unknown error",
      code: parsedError.code,
      stack: error instanceof Error ? error.stack : undefined,
    });

    return NextResponse.json(createErrorResponse(parsedError), { status: 500 });
  }
}

// Wrap with CSRF protection
export const POST = withCSRF(handleChangePlan);
