import { NextRequest, NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { prisma } from "@/lib/db";
import { validateEmail } from "@/lib/input-validation";
import { logger } from "@/lib/logger";
import { getStripePriceId, getSetupFeePriceId } from "@/lib/stripe-prices";
import {
  CheckoutException,
  CheckoutErrorCode,
  createCheckoutError,
  parseCheckoutError,
} from "@/lib/errors/checkout-errors";

/**
 * POST /api/signup/checkout
 *
 * Creates a Stripe checkout session for clinic subscription signup
 * Combines setup fee (one-time) + recurring subscription (monthly/yearly)
 *
 * Request body:
 * {
 *   userId: string,
 *   tierId: "tier1" | "tier2" | "tier3" | "tier4" | "tier5",
 *   stripePriceId: string,
 *   billingPeriod: "monthly" | "yearly",
 *   currency: string,
 *   email: string,
 *   companyName: string,
 *   goals: string[]
 * }
 */

interface CheckoutRequestBody {
  userId: string;
  tierId: string;
  stripePriceId: string;
  billingPeriod: string;
  currency: string;
  email: string;
  companyName: string;
  goals?: string[];
  language?: string;
}

// Setup fee: 919 QAR (in cents)
// CRITICAL: Only QAR is supported until multi-currency infrastructure is built
// Multi-currency requires:
// 1. Database schema with currency-specific price IDs
// 2. Stripe products created for each currency/tier combination
// 3. Both subscription and setup fee must use the SAME currency
const SETUP_FEE_QAR = 91900; // 919 QAR in cents
const ALLOWED_CURRENCIES = ["QAR"];

const SETUP_FEES = {
  tier1: SETUP_FEE_QAR,
  tier2: SETUP_FEE_QAR,
  tier3: SETUP_FEE_QAR,
  tier4: SETUP_FEE_QAR,
  tier5: SETUP_FEE_QAR,
};

export async function POST(request: NextRequest) {
  const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  const requestLogger = logger.scope(`checkout:${requestId}`);

  try {
    // Parse request body
    const body = (await request.json()) as CheckoutRequestBody;

    requestLogger.info("Checkout request received", {
      userId: body.userId,
      tierId: body.tierId,
      billingPeriod: body.billingPeriod,
      currency: body.currency,
      hasEmail: !!body.email,
      hasCompanyName: !!body.companyName,
    });

    // Input validation
    const validationErrors = validateCheckoutInput(body);
    if (validationErrors.length > 0) {
      requestLogger.warn("Checkout validation failed", {
        errors: validationErrors,
        userId: body.userId,
      });

      // Determine specific error code based on validation failures
      let errorCode = CheckoutErrorCode.UNKNOWN_ERROR;
      if (validationErrors.some((e) => e.includes("userId"))) {
        errorCode = CheckoutErrorCode.MISSING_USER_ID;
      } else if (validationErrors.some((e) => e.includes("tierId"))) {
        errorCode = CheckoutErrorCode.INVALID_TIER;
      } else if (validationErrors.some((e) => e.includes("billingPeriod"))) {
        errorCode = CheckoutErrorCode.INVALID_BILLING_PERIOD;
      } else if (validationErrors.some((e) => e.includes("currency"))) {
        errorCode = CheckoutErrorCode.INVALID_CURRENCY;
      } else if (validationErrors.some((e) => e.includes("email"))) {
        errorCode = CheckoutErrorCode.MISSING_EMAIL;
      }

      const error = createCheckoutError(errorCode, {
        validationErrors,
        requestId,
      });

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          details: validationErrors,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 400 },
      );
    }

    const {
      userId,
      tierId,
      stripePriceId,
      billingPeriod,
      currency,
      email,
      companyName,
      goals = [],
    } = body;

    // Determine user's language preference (default to 'ar')
    const userLanguage = body.language || "ar";

    // SECURITY: Validate stripePriceId against database to prevent price tampering
    // This also validates currency (only QAR is supported)
    let expectedPriceId: string | null;
    try {
      expectedPriceId = await getStripePriceId(
        tierId,
        billingPeriod as "monthly" | "yearly",
        currency,
      );
    } catch (priceError) {
      // Check if it's a multi-currency error
      if (
        priceError instanceof Error &&
        priceError.message.includes("Multi-currency not yet supported")
      ) {
        requestLogger.warn("Non-QAR currency requested", {
          userId,
          requestedCurrency: currency,
          tierId,
          billingPeriod,
        });

        const error = createCheckoutError(CheckoutErrorCode.INVALID_CURRENCY, {
          requestedCurrency: currency,
          supportedCurrencies: ["QAR"],
          reason: "Multi-currency not yet supported",
          requestId,
        });

        return NextResponse.json(
          {
            error: error.userMessage,
            errorAr: error.userMessageAr,
            code: error.code,
            retryable: error.retryable,
            recoverySteps: error.recoverySteps,
            recoveryStepsAr: error.recoveryStepsAr,
          },
          { status: 400 },
        );
      }

      // Other error - log and return generic price not found
      requestLogger.error("Error fetching price ID", priceError);
      const error = createCheckoutError(CheckoutErrorCode.PRICE_NOT_FOUND, {
        tierId,
        billingPeriod,
        currency,
        requestId,
        error:
          priceError instanceof Error ? priceError.message : String(priceError),
      });

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 404 },
      );
    }

    if (!expectedPriceId) {
      requestLogger.error("No price found for tier/period/currency", {
        tierId,
        billingPeriod,
        currency,
      });

      const error = createCheckoutError(CheckoutErrorCode.PRICE_NOT_FOUND, {
        tierId,
        billingPeriod,
        currency,
        requestId,
      });

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 404 },
      );
    }

    if (stripePriceId !== expectedPriceId) {
      requestLogger.warn("Price tampering detected", {
        userId,
        providedPriceId: stripePriceId,
        expectedPriceId,
        tierId,
        billingPeriod,
        currency,
      });

      const error = createCheckoutError(CheckoutErrorCode.INVALID_TIER, {
        reason: "Price ID does not match selected tier/period/currency",
        requestId,
      });

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 400 },
      );
    }

    requestLogger.info("Price validation passed", {
      tierId,
      billingPeriod,
      currency,
      stripePriceId,
    });

    // Verify user exists in database
    const user = await prisma.users.findUnique({
      where: { id: userId },
      select: { id: true, email: true, stripeCustomerId: true },
    });

    if (!user) {
      requestLogger.warn("User not found for checkout", { userId });

      const error = createCheckoutError(CheckoutErrorCode.USER_NOT_FOUND, {
        userId,
        requestId,
      });

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 404 },
      );
    }

    // Get setup fee for tier and convert to user's currency
    const baseFeeAmount = SETUP_FEES[tierId as keyof typeof SETUP_FEES];
    if (!baseFeeAmount) {
      requestLogger.error("Invalid tier for checkout", { tierId });

      const error = createCheckoutError(CheckoutErrorCode.INVALID_TIER, {
        tierId,
        requestId,
      });

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 400 },
      );
    }

    // Setup fee amount (QAR only)
    const setupFeeAmount = baseFeeAmount; // Always QAR since currency is validated
    requestLogger.info("Setup fee amount", {
      setupFeeAmount,
      currency,
    });

    // Get or create Stripe customer
    let customerId = user.stripeCustomerId;
    let customerWasCreated = false;
    if (!customerId) {
      try {
        const customer = await stripe.customers.create({
          email,
          name: companyName,
          metadata: {
            user_id: userId,
            company_name: companyName,
            billing_period: billingPeriod,
          },
        });
        customerId = customer.id;
        customerWasCreated = true;

        requestLogger.info("Stripe customer created", { customerId, userId });
      } catch (stripeError) {
        requestLogger.error("Failed to create Stripe customer", stripeError);

        const error = createCheckoutError(
          CheckoutErrorCode.STRIPE_CUSTOMER_CREATION_FAILED,
          {
            userId,
            email,
            requestId,
            stripeError:
              stripeError instanceof Error
                ? stripeError.message
                : String(stripeError),
          },
        );

        return NextResponse.json(
          {
            error: error.userMessage,
            errorAr: error.userMessageAr,
            code: error.code,
            retryable: error.retryable,
            recoverySteps: error.recoverySteps,
            recoveryStepsAr: error.recoveryStepsAr,
          },
          { status: 500 },
        );
      }

      // Save customer ID to user record
      await prisma.users.update({
        where: { id: userId },
        data: { stripeCustomerId: customerId },
      });
    }

    // Only add setup fee for brand new customers.
    // Use pre-created setup fee prices to avoid Stripe Search API eventual consistency issues
    const includeSetupFee = customerWasCreated;
    let setupPriceId: string | null = null;
    if (includeSetupFee) {
      // Get the pre-created setup fee price ID from config
      setupPriceId = getSetupFeePriceId(tierId);

      if (!setupPriceId) {
        requestLogger.error("Setup fee price ID not found in config", {
          tierId,
        });
        const error = createCheckoutError(CheckoutErrorCode.PRICE_NOT_FOUND, {
          tierId,
          type: "setup_fee",
          requestId,
        });
        return NextResponse.json(
          {
            error: error.userMessage,
            errorAr: error.userMessageAr,
            code: error.code,
            retryable: error.retryable,
            recoverySteps: error.recoverySteps,
            recoveryStepsAr: error.recoveryStepsAr,
          },
          { status: 404 },
        );
      }

      requestLogger.info("Using pre-created setup fee price", {
        priceId: setupPriceId,
        tierId,
        userId,
      });
    }

    // Create checkout session with setup fee + subscription
    let checkoutSession;
    try {
      checkoutSession = await stripe.checkout.sessions.create({
        mode: "subscription",
        customer: customerId,
        line_items:
          includeSetupFee && setupPriceId
            ? [
                {
                  price: stripePriceId,
                  quantity: 1,
                },
                {
                  price: setupPriceId,
                  quantity: 1,
                },
              ]
            : [
                {
                  price: stripePriceId,
                  quantity: 1,
                },
              ],
        // CRITICAL: Set metadata on BOTH session AND subscription for proper tracking
        metadata: {
          user_id: userId,
          tier_id: tierId,
          billing_period: billingPeriod,
          ...(includeSetupFee && {
            has_setup_fee: "true",
            setup_fee_amount: setupFeeAmount.toString(),
            setup_fee_currency: currency.toUpperCase(),
          }),
          goals: JSON.stringify(goals),
          language: userLanguage,
        },
        subscription_data: {
          metadata: {
            user_id: userId,
            tier_id: tierId,
            billing_period: billingPeriod,
            ...(includeSetupFee && {
              has_setup_fee: "true",
              setup_fee_amount: setupFeeAmount.toString(),
              setup_fee_currency: currency.toUpperCase(),
            }),
            goals: JSON.stringify(goals),
            language: userLanguage,
          },
          trial_period_days: 14, // 14-day free trial
        },
        success_url: `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:9000"}/${userLanguage}/signup/success?session_id={CHECKOUT_SESSION_ID}`,
        cancel_url: `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:9000"}/${userLanguage}/signup`,
        allow_promotion_codes: true,
        payment_method_types: ["card"],
        billing_address_collection: "required",
      });

      requestLogger.info("Stripe checkout session created", {
        sessionId: checkoutSession.id,
        userId,
        tierId,
        setupFeeAmount,
      });
    } catch (stripeError) {
      requestLogger.error("Failed to create checkout session", stripeError, {
        userId,
        tierId,
        billingPeriod,
        currency,
      });

      const error = createCheckoutError(
        CheckoutErrorCode.STRIPE_SESSION_CREATION_FAILED,
        {
          userId,
          tierId,
          billingPeriod,
          requestId,
          stripeError:
            stripeError instanceof Error
              ? stripeError.message
              : String(stripeError),
        },
      );

      return NextResponse.json(
        {
          error: error.userMessage,
          errorAr: error.userMessageAr,
          code: error.code,
          retryable: error.retryable,
          recoverySteps: error.recoverySteps,
          recoveryStepsAr: error.recoveryStepsAr,
        },
        { status: 500 },
      );
    }

    // Store checkout session ID in user metadata for tracking
    await prisma.users
      .update({
        where: { id: userId },
        data: {
          goals: {
            checkout_session_id: checkoutSession.id,
            tier_id: tierId,
            goals: goals,
            billing_period: billingPeriod,
            currency: currency,
          },
        },
      })
      .catch((err) => {
        logger.warn("Could not store checkout session metadata", {
          error: err,
          userId,
          sessionId: checkoutSession.id,
        });
        // Continue anyway - session exists in Stripe
      });

    return NextResponse.json({
      sessionUrl: checkoutSession.url,
      sessionId: checkoutSession.id,
      success: true,
    });
  } catch (error) {
    // Handle CheckoutException
    if (error instanceof CheckoutException) {
      requestLogger.error("Checkout exception", error.originalError, {
        code: error.error.code,
        retryable: error.error.retryable,
      });

      return NextResponse.json(
        {
          error: error.error.userMessage,
          errorAr: error.error.userMessageAr,
          code: error.error.code,
          retryable: error.error.retryable,
          recoverySteps: error.error.recoverySteps,
          recoveryStepsAr: error.error.recoveryStepsAr,
        },
        { status: 500 },
      );
    }

    // Handle unknown errors
    requestLogger.error("Unexpected checkout error", error, {
      type: error instanceof Error ? error.constructor.name : typeof error,
    });

    const checkoutError = parseCheckoutError(error, { requestId });

    return NextResponse.json(
      {
        error: checkoutError.error.userMessage,
        errorAr: checkoutError.error.userMessageAr,
        code: checkoutError.error.code,
        retryable: checkoutError.error.retryable,
        recoverySteps: checkoutError.error.recoverySteps,
        recoveryStepsAr: checkoutError.error.recoveryStepsAr,
      },
      { status: 500 },
    );
  }
}

/**
 * Validate checkout request input
 */
function validateCheckoutInput(body: Partial<CheckoutRequestBody>): string[] {
  const errors: string[] = [];

  if (!body.userId) {
    errors.push("userId is required");
  } else if (typeof body.userId !== "string") {
    errors.push("userId must be a string");
  }

  if (!body.tierId) {
    errors.push("tierId is required");
  } else if (
    !["tier1", "tier2", "tier3", "tier4", "tier5"].includes(body.tierId)
  ) {
    errors.push("tierId must be tier1-tier5");
  }

  if (!body.stripePriceId) {
    errors.push("stripePriceId is required");
  } else if (typeof body.stripePriceId !== "string") {
    errors.push("stripePriceId must be a string");
  }

  if (!body.billingPeriod) {
    errors.push("billingPeriod is required");
  } else if (!["monthly", "yearly"].includes(body.billingPeriod)) {
    errors.push("billingPeriod must be monthly or yearly");
  }

  if (!body.currency) {
    errors.push("currency is required");
  } else if (typeof body.currency !== "string" || body.currency.length !== 3) {
    errors.push("currency must be a 3-letter code (e.g., aed, usd)");
  } else if (!ALLOWED_CURRENCIES.includes(body.currency.toUpperCase())) {
    errors.push(`currency must be one of: ${ALLOWED_CURRENCIES.join(", ")}`);
  }

  if (!body.email) {
    errors.push("email is required");
  } else if (!validateEmail(body.email).valid) {
    errors.push("email format is invalid");
  }

  if (!body.companyName) {
    errors.push("companyName is required");
  } else if (
    typeof body.companyName !== "string" ||
    body.companyName.length < 2
  ) {
    errors.push("companyName must be at least 2 characters");
  }

  return errors;
}
