/**
 * Checkout Error Types and Utilities
 *
 * Provides structured error handling for the checkout flow
 * with user-friendly messages and recovery guidance
 */

export enum CheckoutErrorCode {
  // Validation Errors
  MISSING_USER_ID = "MISSING_USER_ID",
  MISSING_PLAN_DATA = "MISSING_PLAN_DATA",
  MISSING_EMAIL = "MISSING_EMAIL",
  INVALID_TIER = "INVALID_TIER",
  INVALID_BILLING_PERIOD = "INVALID_BILLING_PERIOD",
  INVALID_CURRENCY = "INVALID_CURRENCY",

  // API Errors
  USER_NOT_FOUND = "USER_NOT_FOUND",
  PRICE_NOT_FOUND = "PRICE_NOT_FOUND",
  STRIPE_CUSTOMER_CREATION_FAILED = "STRIPE_CUSTOMER_CREATION_FAILED",
  STRIPE_SESSION_CREATION_FAILED = "STRIPE_SESSION_CREATION_FAILED",

  // Network Errors
  NETWORK_ERROR = "NETWORK_ERROR",
  TIMEOUT_ERROR = "TIMEOUT_ERROR",

  // System Errors
  DATABASE_ERROR = "DATABASE_ERROR",
  UNKNOWN_ERROR = "UNKNOWN_ERROR",
}

export interface CheckoutError {
  code: CheckoutErrorCode;
  message: string;
  userMessage: string;
  userMessageAr: string;
  recoverySteps: string[];
  recoveryStepsAr: string[];
  retryable: boolean;
  metadata?: Record<string, unknown>;
}

export class CheckoutException extends Error {
  constructor(
    public readonly error: CheckoutError,
    public readonly originalError?: Error,
  ) {
    super(error.message);
    this.name = "CheckoutException";

    // Maintain proper stack trace
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, CheckoutException);
    }
  }

  toJSON() {
    return {
      code: this.error.code,
      message: this.error.message,
      userMessage: this.error.userMessage,
      userMessageAr: this.error.userMessageAr,
      recoverySteps: this.error.recoverySteps,
      recoveryStepsAr: this.error.recoveryStepsAr,
      retryable: this.error.retryable,
      metadata: this.error.metadata,
    };
  }
}

/**
 * Error definitions with user-friendly messages and recovery steps
 */
export const CHECKOUT_ERRORS: Record<
  CheckoutErrorCode,
  Omit<CheckoutError, "code" | "metadata">
> = {
  [CheckoutErrorCode.MISSING_USER_ID]: {
    message: "User ID is missing from signup context",
    userMessage: "Your session has expired. Please sign up again.",
    userMessageAr: "انتهت صلاحية جلستك. يرجى التسجيل مرة أخرى.",
    recoverySteps: [
      "Return to the signup page",
      "Complete the signup process",
      "Verify your email",
      "Continue to checkout",
    ],
    recoveryStepsAr: [
      "العودة إلى صفحة التسجيل",
      "أكمل عملية التسجيل",
      "تحقق من بريدك الإلكتروني",
      "تابع إلى الدفع",
    ],
    retryable: false,
  },

  [CheckoutErrorCode.MISSING_PLAN_DATA]: {
    message: "Plan selection data is missing",
    userMessage: "Please select a plan before proceeding to checkout.",
    userMessageAr: "يرجى اختيار خطة قبل المتابعة إلى الدفع.",
    recoverySteps: [
      "Go back to plan selection",
      "Choose your preferred plan",
      "Continue to checkout",
    ],
    recoveryStepsAr: [
      "العودة إلى اختيار الخطة",
      "اختر الخطة المفضلة لديك",
      "تابع إلى الدفع",
    ],
    retryable: false,
  },

  [CheckoutErrorCode.MISSING_EMAIL]: {
    message: "Email address is missing",
    userMessage: "Your email address is required. Please sign up again.",
    userMessageAr: "عنوان بريدك الإلكتروني مطلوب. يرجى التسجيل مرة أخرى.",
    recoverySteps: [
      "Return to signup",
      "Enter your email address",
      "Complete verification",
    ],
    recoveryStepsAr: [
      "العودة إلى التسجيل",
      "أدخل عنوان بريدك الإلكتروني",
      "أكمل التحقق",
    ],
    retryable: false,
  },

  [CheckoutErrorCode.INVALID_TIER]: {
    message: "Invalid subscription tier selected",
    userMessage:
      "The selected plan is not available. Please choose another plan.",
    userMessageAr: "الخطة المحددة غير متاحة. يرجى اختيار خطة أخرى.",
    recoverySteps: [
      "Go back to plan selection",
      "Choose a valid plan (Tier 1-5)",
      "Continue to checkout",
    ],
    recoveryStepsAr: [
      "العودة إلى اختيار الخطة",
      "اختر خطة صالحة (المستوى 1-5)",
      "تابع إلى الدفع",
    ],
    retryable: false,
  },

  [CheckoutErrorCode.INVALID_BILLING_PERIOD]: {
    message: "Invalid billing period selected",
    userMessage: "Please select either monthly or yearly billing.",
    userMessageAr: "يرجى اختيار الفوترة الشهرية أو السنوية.",
    recoverySteps: [
      "Go back to plan selection",
      "Choose monthly or yearly billing",
      "Continue to checkout",
    ],
    recoveryStepsAr: [
      "العودة إلى اختيار الخطة",
      "اختر الفوترة الشهرية أو السنوية",
      "تابع إلى الدفع",
    ],
    retryable: false,
  },

  [CheckoutErrorCode.INVALID_CURRENCY]: {
    message: "Invalid currency selected",
    userMessage:
      "Currently only QAR (Qatari Riyal) is supported. Multi-currency support is coming soon.",
    userMessageAr:
      "حالياً فقط الريال القطري (QAR) مدعوم. دعم العملات المتعددة قريباً.",
    recoverySteps: [
      "Refresh the page",
      "Select QAR (Qatari Riyal) as currency",
      "Continue to checkout",
      "Contact sales for other currency options",
    ],
    recoveryStepsAr: [
      "قم بتحديث الصفحة",
      "اختر الريال القطري (QAR) كعملة",
      "تابع إلى الدفع",
      "اتصل بالمبيعات لخيارات العملات الأخرى",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.USER_NOT_FOUND]: {
    message: "User account not found in database",
    userMessage: "Your account could not be found. Please sign up again.",
    userMessageAr: "لم يتم العثور على حسابك. يرجى التسجيل مرة أخرى.",
    recoverySteps: [
      "Return to signup page",
      "Create a new account",
      "Verify your email",
      "Continue to checkout",
    ],
    recoveryStepsAr: [
      "العودة إلى صفحة التسجيل",
      "إنشاء حساب جديد",
      "تحقق من بريدك الإلكتروني",
      "تابع إلى الدفع",
    ],
    retryable: false,
  },

  [CheckoutErrorCode.PRICE_NOT_FOUND]: {
    message: "Stripe price ID not found for selected plan",
    userMessage:
      "The pricing information for this plan is temporarily unavailable.",
    userMessageAr: "معلومات التسعير لهذه الخطة غير متوفرة مؤقتاً.",
    recoverySteps: [
      "Try again in a few moments",
      "Select a different plan",
      "Contact support if the issue persists",
    ],
    recoveryStepsAr: [
      "حاول مرة أخرى بعد لحظات",
      "اختر خطة مختلفة",
      "اتصل بالدعم إذا استمرت المشكلة",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.STRIPE_CUSTOMER_CREATION_FAILED]: {
    message: "Failed to create Stripe customer",
    userMessage:
      "We encountered an issue setting up your payment account. Please try again.",
    userMessageAr:
      "واجهنا مشكلة في إعداد حساب الدفع الخاص بك. يرجى المحاولة مرة أخرى.",
    recoverySteps: [
      "Refresh the page",
      "Try again",
      "Contact support if the issue continues",
    ],
    recoveryStepsAr: [
      "قم بتحديث الصفحة",
      "حاول مرة أخرى",
      "اتصل بالدعم إذا استمرت المشكلة",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.STRIPE_SESSION_CREATION_FAILED]: {
    message: "Failed to create Stripe checkout session",
    userMessage:
      "We could not process your checkout request. Please try again.",
    userMessageAr:
      "لم نتمكن من معالجة طلب الدفع الخاص بك. يرجى المحاولة مرة أخرى.",
    recoverySteps: [
      "Wait a moment and try again",
      "Check your internet connection",
      "Contact support if the problem persists",
    ],
    recoveryStepsAr: [
      "انتظر لحظة وحاول مرة أخرى",
      "تحقق من اتصالك بالإنترنت",
      "اتصل بالدعم إذا استمرت المشكلة",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.NETWORK_ERROR]: {
    message: "Network request failed",
    userMessage: "Connection error. Please check your internet and try again.",
    userMessageAr:
      "خطأ في الاتصال. يرجى التحقق من الإنترنت والمحاولة مرة أخرى.",
    recoverySteps: [
      "Check your internet connection",
      "Refresh the page",
      "Try again",
    ],
    recoveryStepsAr: [
      "تحقق من اتصالك بالإنترنت",
      "قم بتحديث الصفحة",
      "حاول مرة أخرى",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.TIMEOUT_ERROR]: {
    message: "Request timed out",
    userMessage: "The request took too long. Please try again.",
    userMessageAr: "استغرق الطلب وقتاً طويلاً. يرجى المحاولة مرة أخرى.",
    recoverySteps: ["Check your internet speed", "Wait a moment", "Try again"],
    recoveryStepsAr: [
      "تحقق من سرعة الإنترنت لديك",
      "انتظر لحظة",
      "حاول مرة أخرى",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.DATABASE_ERROR]: {
    message: "Database operation failed",
    userMessage: "A system error occurred. Our team has been notified.",
    userMessageAr: "حدث خطأ في النظام. تم إخطار فريقنا.",
    recoverySteps: [
      "Wait a few moments",
      "Try again",
      "Contact support if the issue continues",
    ],
    recoveryStepsAr: [
      "انتظر بضع لحظات",
      "حاول مرة أخرى",
      "اتصل بالدعم إذا استمرت المشكلة",
    ],
    retryable: true,
  },

  [CheckoutErrorCode.UNKNOWN_ERROR]: {
    message: "An unknown error occurred",
    userMessage: "Something went wrong. Please try again or contact support.",
    userMessageAr: "حدث خطأ ما. يرجى المحاولة مرة أخرى أو الاتصال بالدعم.",
    recoverySteps: [
      "Refresh the page",
      "Try again",
      "Contact support with error details",
    ],
    recoveryStepsAr: [
      "قم بتحديث الصفحة",
      "حاول مرة أخرى",
      "اتصل بالدعم مع تفاصيل الخطأ",
    ],
    retryable: true,
  },
};

/**
 * Create a checkout error with metadata
 */
export function createCheckoutError(
  code: CheckoutErrorCode,
  metadata?: Record<string, unknown>,
): CheckoutError {
  return {
    code,
    ...CHECKOUT_ERRORS[code],
    metadata,
  };
}

/**
 * Parse an error into a CheckoutException
 */
export function parseCheckoutError(
  error: unknown,
  context?: Record<string, unknown>,
): CheckoutException {
  // If already a CheckoutException, return it
  if (error instanceof CheckoutException) {
    return error;
  }

  // If it's an Error with a known message pattern
  if (error instanceof Error) {
    const message = error.message.toLowerCase();

    // Map common error messages to error codes
    if (message.includes("user not found")) {
      return new CheckoutException(
        createCheckoutError(CheckoutErrorCode.USER_NOT_FOUND, context),
        error,
      );
    }

    if (
      message.includes("price") &&
      (message.includes("not found") || message.includes("invalid"))
    ) {
      return new CheckoutException(
        createCheckoutError(CheckoutErrorCode.PRICE_NOT_FOUND, context),
        error,
      );
    }

    if (
      message.includes("network") ||
      message.includes("fetch") ||
      message.includes("econnrefused")
    ) {
      return new CheckoutException(
        createCheckoutError(CheckoutErrorCode.NETWORK_ERROR, context),
        error,
      );
    }

    if (message.includes("timeout") || message.includes("timed out")) {
      return new CheckoutException(
        createCheckoutError(CheckoutErrorCode.TIMEOUT_ERROR, context),
        error,
      );
    }

    if (message.includes("database") || message.includes("prisma")) {
      return new CheckoutException(
        createCheckoutError(CheckoutErrorCode.DATABASE_ERROR, context),
        error,
      );
    }

    if (message.includes("stripe") && message.includes("customer")) {
      return new CheckoutException(
        createCheckoutError(
          CheckoutErrorCode.STRIPE_CUSTOMER_CREATION_FAILED,
          context,
        ),
        error,
      );
    }

    if (message.includes("stripe") || message.includes("checkout")) {
      return new CheckoutException(
        createCheckoutError(
          CheckoutErrorCode.STRIPE_SESSION_CREATION_FAILED,
          context,
        ),
        error,
      );
    }
  }

  // Unknown error
  return new CheckoutException(
    createCheckoutError(CheckoutErrorCode.UNKNOWN_ERROR, {
      ...context,
      originalError: error instanceof Error ? error.message : String(error),
    }),
    error instanceof Error ? error : undefined,
  );
}

/**
 * Validate checkout data and throw CheckoutException if invalid
 */
export function validateCheckoutData(data: {
  userId?: string;
  email?: string;
  plan?: {
    tierId?: string;
    billingPeriod?: string;
    currency?: string;
  };
}): void {
  if (!data.userId) {
    throw new CheckoutException(
      createCheckoutError(CheckoutErrorCode.MISSING_USER_ID),
    );
  }

  if (!data.email) {
    throw new CheckoutException(
      createCheckoutError(CheckoutErrorCode.MISSING_EMAIL),
    );
  }

  if (!data.plan) {
    throw new CheckoutException(
      createCheckoutError(CheckoutErrorCode.MISSING_PLAN_DATA),
    );
  }

  const validTiers = ["tier1", "tier2", "tier3", "tier4", "tier5"];
  if (!data.plan.tierId || !validTiers.includes(data.plan.tierId)) {
    throw new CheckoutException(
      createCheckoutError(CheckoutErrorCode.INVALID_TIER, {
        providedTier: data.plan.tierId,
      }),
    );
  }

  const validPeriods = ["monthly", "yearly"];
  if (
    !data.plan.billingPeriod ||
    !validPeriods.includes(data.plan.billingPeriod)
  ) {
    throw new CheckoutException(
      createCheckoutError(CheckoutErrorCode.INVALID_BILLING_PERIOD, {
        providedPeriod: data.plan.billingPeriod,
      }),
    );
  }

  // CRITICAL: Only QAR is supported until multi-currency infrastructure is built
  const validCurrencies = ["QAR", "qar"];
  if (!data.plan.currency || !validCurrencies.includes(data.plan.currency)) {
    throw new CheckoutException(
      createCheckoutError(CheckoutErrorCode.INVALID_CURRENCY, {
        providedCurrency: data.plan.currency,
        reason: "Only QAR (Qatari Riyal) is currently supported",
      }),
    );
  }
}
