/**
 * E2E Tests for Checkout Error Handling
 *
 * Tests error scenarios in the checkout flow to ensure
 * users receive clear, actionable error messages
 */

import { describe, it, expect, beforeEach, jest } from "@jest/globals";
import { signupStorage } from "@/lib/storage";
import {
  CheckoutErrorCode,
  validateCheckoutData,
  parseCheckoutError,
  CheckoutException,
} from "@/lib/errors/checkout-errors";

describe("Checkout Error Handling", () => {
  beforeEach(() => {
    // Clear localStorage before each test
    signupStorage.clear();
    global.fetch = jest.fn() as any;
  });

  describe("Validation Errors", () => {
    it("should detect missing user ID", () => {
      expect(() => {
        validateCheckoutData({
          userId: undefined,
          email: "test@example.com",
          plan: {
            tierId: "tier1",
            billingPeriod: "monthly",
            currency: "QAR",
          },
        });
      }).toThrow(CheckoutException);

      try {
        validateCheckoutData({
          userId: undefined,
          email: "test@example.com",
          plan: {
            tierId: "tier1",
            billingPeriod: "monthly",
            currency: "QAR",
          },
        });
      } catch (error) {
        expect(error).toBeInstanceOf(CheckoutException);
        const checkoutError = error as CheckoutException;
        expect(checkoutError.error.code).toBe(
          CheckoutErrorCode.MISSING_USER_ID,
        );
        expect(checkoutError.error.retryable).toBe(false);
        expect(checkoutError.error.recoverySteps).toHaveLength(4);
      }
    });

    it("should detect missing email", () => {
      expect(() => {
        validateCheckoutData({
          userId: "user123",
          email: undefined,
          plan: {
            tierId: "tier1",
            billingPeriod: "monthly",
            currency: "QAR",
          },
        });
      }).toThrow(CheckoutException);
    });

    it("should detect missing plan data", () => {
      expect(() => {
        validateCheckoutData({
          userId: "user123",
          email: "test@example.com",
          plan: undefined,
        });
      }).toThrow(CheckoutException);
    });

    it("should detect invalid tier", () => {
      expect(() => {
        validateCheckoutData({
          userId: "user123",
          email: "test@example.com",
          plan: {
            tierId: "invalid_tier",
            billingPeriod: "monthly",
            currency: "QAR",
          },
        });
      }).toThrow(CheckoutException);
    });

    it("should detect invalid billing period", () => {
      expect(() => {
        validateCheckoutData({
          userId: "user123",
          email: "test@example.com",
          plan: {
            tierId: "tier1",
            billingPeriod: "weekly" as any,
            currency: "QAR",
          },
        });
      }).toThrow(CheckoutException);
    });

    it("should detect invalid currency", () => {
      expect(() => {
        validateCheckoutData({
          userId: "user123",
          email: "test@example.com",
          plan: {
            tierId: "tier1",
            billingPeriod: "monthly",
            currency: "INVALID",
          },
        });
      }).toThrow(CheckoutException);
    });

    it("should pass validation with valid data", () => {
      expect(() => {
        validateCheckoutData({
          userId: "user123",
          email: "test@example.com",
          plan: {
            tierId: "tier1",
            billingPeriod: "monthly",
            currency: "QAR",
          },
        });
      }).not.toThrow();
    });
  });

  describe("Error Parsing", () => {
    it("should parse user not found error", () => {
      const error = new Error("User not found");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(CheckoutErrorCode.USER_NOT_FOUND);
      expect(parsed.error.retryable).toBe(false);
      expect(parsed.error.userMessage).toContain("account could not be found");
    });

    it("should parse price not found error", () => {
      const error = new Error("Price not found for tier");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(CheckoutErrorCode.PRICE_NOT_FOUND);
      expect(parsed.error.retryable).toBe(true);
      expect(parsed.error.userMessage).toContain("pricing information");
    });

    it("should parse network error", () => {
      const error = new Error("Network request failed");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(CheckoutErrorCode.NETWORK_ERROR);
      expect(parsed.error.retryable).toBe(true);
      expect(parsed.error.userMessage).toContain("Connection error");
    });

    it("should parse timeout error", () => {
      const error = new Error("Request timed out");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(CheckoutErrorCode.TIMEOUT_ERROR);
      expect(parsed.error.retryable).toBe(true);
    });

    it("should parse database error", () => {
      const error = new Error("Database connection failed");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(CheckoutErrorCode.DATABASE_ERROR);
      expect(parsed.error.retryable).toBe(true);
    });

    it("should parse Stripe customer creation error", () => {
      const error = new Error("Stripe customer creation failed");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(
        CheckoutErrorCode.STRIPE_CUSTOMER_CREATION_FAILED,
      );
      expect(parsed.error.retryable).toBe(true);
    });

    it("should parse Stripe checkout session error", () => {
      const error = new Error("Stripe checkout session failed");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(
        CheckoutErrorCode.STRIPE_SESSION_CREATION_FAILED,
      );
      expect(parsed.error.retryable).toBe(true);
    });

    it("should parse unknown error", () => {
      const error = new Error("Something completely unexpected");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.code).toBe(CheckoutErrorCode.UNKNOWN_ERROR);
      expect(parsed.error.retryable).toBe(true);
      expect(parsed.error.metadata?.originalError).toBe(
        "Something completely unexpected",
      );
    });

    it("should preserve CheckoutException", () => {
      const original = new CheckoutException({
        code: CheckoutErrorCode.INVALID_TIER,
        message: "Invalid tier",
        userMessage: "Plan not available",
        userMessageAr: "الخطة غير متاحة",
        recoverySteps: ["Step 1"],
        recoveryStepsAr: ["خطوة 1"],
        retryable: false,
      });

      const parsed = parseCheckoutError(original);

      expect(parsed).toBe(original);
    });
  });

  describe("Error Messages - Bilingual Support", () => {
    it("should provide English and Arabic error messages", () => {
      const error = new CheckoutException({
        code: CheckoutErrorCode.PRICE_NOT_FOUND,
        message: "Price not found",
        userMessage:
          "The pricing information for this plan is temporarily unavailable.",
        userMessageAr: "معلومات التسعير لهذه الخطة غير متوفرة مؤقتاً.",
        recoverySteps: ["Try again"],
        recoveryStepsAr: ["حاول مرة أخرى"],
        retryable: true,
      });

      expect(error.error.userMessage).toBeTruthy();
      expect(error.error.userMessageAr).toBeTruthy();
      expect(error.error.recoverySteps).toHaveLength(1);
      expect(error.error.recoveryStepsAr).toHaveLength(1);
    });

    it("should provide recovery steps in both languages", () => {
      try {
        validateCheckoutData({
          userId: undefined,
          email: "test@example.com",
          plan: {
            tierId: "tier1",
            billingPeriod: "monthly",
            currency: "QAR",
          },
        });
      } catch (error) {
        const checkoutError = error as CheckoutException;
        expect(checkoutError.error.recoverySteps.length).toBeGreaterThan(0);
        expect(checkoutError.error.recoveryStepsAr.length).toBeGreaterThan(0);
        expect(checkoutError.error.recoverySteps.length).toBe(
          checkoutError.error.recoveryStepsAr.length,
        );
      }
    });
  });

  describe("Error Metadata", () => {
    it("should include context in error metadata", () => {
      const error = new Error("Test error");
      const context = {
        userId: "user123",
        tierId: "tier1",
        requestId: "req_123",
      };

      const parsed = parseCheckoutError(error, context);

      expect(parsed.error.metadata).toMatchObject(context);
    });

    it("should include original error message in metadata", () => {
      const error = new Error("Original error message");
      const parsed = parseCheckoutError(error);

      expect(parsed.error.metadata?.originalError).toBe(
        "Original error message",
      );
    });
  });

  describe("Error Serializability", () => {
    it("should serialize CheckoutException to JSON", () => {
      const error = new CheckoutException({
        code: CheckoutErrorCode.NETWORK_ERROR,
        message: "Network failed",
        userMessage: "Connection error",
        userMessageAr: "خطأ في الاتصال",
        recoverySteps: ["Check connection"],
        recoveryStepsAr: ["تحقق من الاتصال"],
        retryable: true,
        metadata: { requestId: "req_123" },
      });

      const json = error.toJSON();

      expect(json.code).toBe(CheckoutErrorCode.NETWORK_ERROR);
      expect(json.userMessage).toBe("Connection error");
      expect(json.userMessageAr).toBe("خطأ في الاتصال");
      expect(json.retryable).toBe(true);
      expect(json.metadata).toEqual({ requestId: "req_123" });
    });
  });

  describe("Retryability Logic", () => {
    it("should mark validation errors as non-retryable", () => {
      const validationErrors = [
        CheckoutErrorCode.MISSING_USER_ID,
        CheckoutErrorCode.MISSING_EMAIL,
        CheckoutErrorCode.MISSING_PLAN_DATA,
        CheckoutErrorCode.INVALID_TIER,
        CheckoutErrorCode.INVALID_BILLING_PERIOD,
        CheckoutErrorCode.USER_NOT_FOUND,
      ];

      validationErrors.forEach((code) => {
        try {
          if (code === CheckoutErrorCode.MISSING_USER_ID) {
            validateCheckoutData({
              userId: undefined,
              email: "test@example.com",
              plan: {
                tierId: "tier1",
                billingPeriod: "monthly",
                currency: "QAR",
              },
            });
          }
        } catch (error) {
          const checkoutError = error as CheckoutException;
          expect(checkoutError.error.retryable).toBe(false);
        }
      });
    });

    it("should mark system errors as retryable", () => {
      const systemErrors = [
        "Network request failed",
        "Request timed out",
        "Database error",
        "Stripe checkout failed",
      ];

      systemErrors.forEach((errorMessage) => {
        const parsed = parseCheckoutError(new Error(errorMessage));
        expect(parsed.error.retryable).toBe(true);
      });
    });
  });
});
