import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

const { mockSend } = vi.hoisted(() => ({
  mockSend: vi.fn()
}));

vi.mock("@aws-sdk/client-secrets-manager", () => {
  return {
    SecretsManagerClient: vi.fn(() => ({
      send: mockSend
    })),
    GetSecretValueCommand: vi.fn((input) => input)
  };
});

const getPaymentType = require("../getPaymentType");
const decodeComment = require("../decodeComment");
const getCptellerUrl = require("../getCptellerUrl");
const handlePaymentValidationError = require("../handlePaymentValidationError");
const getShopCredentials = require("../getShopCredentials");

describe("Payment Processing Functions", () => {
  // DecodeComment Tests
  describe("decodeComment", () => {
    it("decodes a valid base64-encoded JSON comment", () => {
      const encoded = "eyJ0ZXN0IjoiZGF0YSJ9";
      const expected = { test: "data" };
      expect(decodeComment(encoded)).toEqual(expected);
    });

    it("decodes a complex base64-encoded JSON with payments", () => {
      const encoded = "eyJwYXltZW50cyI6W3siam9iaWQiOiIxMjMifV19";
      const expected = { payments: [{ jobid: "123" }] };
      expect(decodeComment(encoded)).toEqual(expected);
    });

    it("returns null when comment is null", () => {
      expect(decodeComment(null)).toBeNull();
    });

    it("returns null when comment is undefined", () => {
      expect(decodeComment(undefined)).toBeNull();
    });

    it("returns null when comment is an empty string", () => {
      expect(decodeComment("")).toBeNull();
    });

    it("returns null when comment is malformed base64", () => {
      expect(decodeComment("!@#$%")).toBeNull();
    });

    it("returns null when comment is valid base64 but not valid JSON", () => {
      expect(decodeComment("aW52YWxpZA==")).toBeNull();
    });
  });

  // GetPaymentType Tests
  describe("getPaymentType", () => {
    it("returns mapped value when card type exists in mapping", () => {
      const ipMapping = { visa: "Visa Card", amex: "American Express" };
      expect(getPaymentType(ipMapping, "visa")).toBe("Visa Card");
    });

    it("returns original value when card type not in mapping", () => {
      const ipMapping = { visa: "Visa Card" };
      expect(getPaymentType(ipMapping, "mastercard")).toBe("mastercard");
    });

    it("handles lowercase conversion", () => {
      const ipMapping = { visa: "Visa Card" };
      expect(getPaymentType(ipMapping, "VISA")).toBe("Visa Card");
    });

    it("handles null mapping", () => {
      expect(getPaymentType(null, "visa")).toBe("visa");
    });

    it("handles undefined mapping", () => {
      expect(getPaymentType(undefined, "visa")).toBe("visa");
    });

    it("handles empty string card type", () => {
      const ipMapping = { visa: "Visa Card" };
      expect(getPaymentType(ipMapping, "")).toBe("");
    });

    it("handles undefined card type", () => {
      const ipMapping = { visa: "Visa Card" };
      expect(getPaymentType(ipMapping, undefined)).toBe(undefined);
    });
  });

  // GetCptellerUrl Tests
  describe("getCptellerUrl", () => {
    const originalEnv = process.env.NODE_ENV;

    afterEach(() => {
      process.env.NODE_ENV = originalEnv;
    });

    it("uses test domain in non-production environment", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({ apiType: "webapi" });
      expect(url).toEqual("https://test.cpteller.com/api/webapi.cfc");
    });

    it("uses secure domain in production environment", () => {
      process.env.NODE_ENV = "production";
      const url = getCptellerUrl({ apiType: "webapi" });
      expect(url).toEqual("https://secure.cpteller.com/api/webapi.cfc");
    });

    it("adds version number for webapi type", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({ apiType: "webapi", version: "26" });
      expect(url).toEqual("https://test.cpteller.com/api/26/webapi.cfc");
    });

    it("constructs custapi URL without version number", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({ apiType: "custapi", version: "26" });
      expect(url).toEqual("https://test.cpteller.com/api/custapi.cfc");
    });

    it("adds query parameters to the URL", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({
        apiType: "webapi",
        params: { method: "payment_refund", test: "value" }
      });
      expect(url).toEqual("https://test.cpteller.com/api/webapi.cfc?method=payment_refund&test=value");
    });

    it("handles empty params object", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({ apiType: "webapi", params: {} });
      expect(url).toEqual("https://test.cpteller.com/api/webapi.cfc");
    });

    it("defaults to webapi when no apiType is provided", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({});
      expect(url).toEqual("https://test.cpteller.com/api/webapi.cfc");
    });

    it("combines version and query parameters correctly", () => {
      process.env.NODE_ENV = "";
      const url = getCptellerUrl({
        apiType: "webapi",
        version: "26",
        params: { method: "fee" }
      });
      expect(url).toEqual("https://test.cpteller.com/api/26/webapi.cfc?method=fee");
    });
  });

  // GetShopCredentials Tests
  describe("getShopCredentials", () => {
    const originalEnv = { ...process.env };

    beforeEach(() => {
      mockSend.mockReset();
      process.env.INTELLIPAY_MERCHANTKEY = "test-merchant-key";
      process.env.INTELLIPAY_APIKEY = "test-api-key";
    });

    afterEach(() => {
      process.env = { ...originalEnv };
    });

    it("returns environment variables in non-production environment", async () => {
      process.env.NODE_ENV = "development";
      const result = await getShopCredentials({ imexshopid: "12345" });
      expect(result).toEqual({
        merchantkey: "test-merchant-key",
        apikey: "test-api-key"
      });
      expect(mockSend).not.toHaveBeenCalled();
    });

    it("returns undefined when imexshopid is missing in production", async () => {
      process.env.NODE_ENV = "production";
      const result = await getShopCredentials({ name: "Test Shop" });
      expect(result).toBeUndefined();
      expect(mockSend).not.toHaveBeenCalled();
    });

    it("returns undefined for null bodyshop in production", async () => {
      process.env.NODE_ENV = "production";
      const result = await getShopCredentials(null);
      expect(result).toBeUndefined();
      expect(mockSend).not.toHaveBeenCalled();
    });

    it("returns undefined for undefined bodyshop in production", async () => {
      process.env.NODE_ENV = "production";
      const result = await getShopCredentials(undefined);
      expect(result).toBeUndefined();
      expect(mockSend).not.toHaveBeenCalled();
    });
  });

  // HandlePaymentValidationError Tests
  describe("handlePaymentValidationError", () => {
    it("logs error and sends 400 response", () => {
      const mockLog = vi.fn();
      const mockLogger = { log: mockLog };
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis()
      };

      const logCode = "test-validation-error";
      const message = "Invalid data";
      const logMeta = { field: "test", value: 123 };

      const result = handlePaymentValidationError(mockRes, mockLogger, logCode, message, logMeta);

      expect(mockLog).toHaveBeenCalledWith(logCode, "ERROR", "api", null, {
        message,
        ...logMeta
      });
      expect(mockRes.status).toHaveBeenCalledWith(400);
      expect(mockRes.send).toHaveBeenCalledWith(`Bad Request: ${message}`);
      expect(result).toBe(mockRes);
    });

    it("formats different error messages correctly", () => {
      const mockLog = vi.fn();
      const mockLogger = { log: mockLog };
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis()
      };

      handlePaymentValidationError(mockRes, mockLogger, "error-code", "Custom error");
      expect(mockRes.send).toHaveBeenCalledWith("Bad Request: Custom error");
    });

    it("passes different logCodes to logger", () => {
      const mockLog = vi.fn();
      const mockLogger = { log: mockLog };
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis()
      };

      handlePaymentValidationError(mockRes, mockLogger, "custom-log-code", "Error message");
      expect(mockLog).toHaveBeenCalledWith("custom-log-code", "ERROR", "api", null, { message: "Error message" });
    });

    it("works with minimal logMeta", () => {
      const mockLog = vi.fn();
      const mockLogger = { log: mockLog };
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis()
      };

      handlePaymentValidationError(mockRes, mockLogger, "error-code", "Error message", {});
      expect(mockLog).toHaveBeenCalledWith("error-code", "ERROR", "api", null, { message: "Error message" });
    });

    it("works with undefined logMeta", () => {
      const mockLog = vi.fn();
      const mockLogger = { log: mockLog };
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis()
      };

      handlePaymentValidationError(mockRes, mockLogger, "error-code", "Error message");
      expect(mockLog).toHaveBeenCalledWith("error-code", "ERROR", "api", null, { message: "Error message" });
    });
  });
});
