import { describe, it, expect, beforeEach, mock } from "bun:test";
import {
  convertToWei,
  isXChainSwapContent,
  validateSquidRouterConfig,
  squidRouterEnvSchema,
} from "../helpers/utils";
import { createMockRuntime } from "./test-utils";
import { z } from "zod";

describe("convertToWei", () => {
  it("should convert whole numbers correctly", () => {
    const token = {
      decimals: 18,
      isNative: true,
      symbol: "ETH",
      address: "0x",
      enabled: true,
    };
    const result = convertToWei("1", token);
    expect(result).toBe("1000000000000000000");
  });

  it("should convert decimal numbers correctly", () => {
    const token = {
      decimals: 6,
      isNative: false,
      symbol: "USDC",
      address: "0x",
      enabled: true,
    };
    const result = convertToWei("100.5", token);
    expect(result).toBe("100500000");
  });

  it("should handle numbers as input", () => {
    const token = {
      decimals: 18,
      isNative: true,
      symbol: "ETH",
      address: "0x",
      enabled: true,
    };
    const result = convertToWei(0.5, token);
    expect(result).toBe("500000000000000000");
  });

  it("should handle zero decimals", () => {
    const token = {
      decimals: 0,
      isNative: false,
      symbol: "TEST",
      address: "0x",
      enabled: true,
    };
    const result = convertToWei("100", token);
    expect(result).toBe("100");
  });

  it("should throw error for invalid decimals", () => {
    const token = {
      decimals: -1,
      isNative: false,
      symbol: "TEST",
      address: "0x",
      enabled: true,
    };
    expect(() => convertToWei("100", token)).toThrow("Invalid decimals value");
  });

  it("should throw error for decimals > 255", () => {
    const token = {
      decimals: 256,
      isNative: false,
      symbol: "TEST",
      address: "0x",
      enabled: true,
    };
    expect(() => convertToWei("100", token)).toThrow("Invalid decimals value");
  });

  it("should throw error for invalid amount", () => {
    const token = {
      decimals: 18,
      isNative: false,
      symbol: "TEST",
      address: "0x",
      enabled: true,
    };
    expect(() => convertToWei("invalid", token)).toThrow(
      "Failed to convert amount"
    );
  });

  it("should handle very small amounts", () => {
    const token = {
      decimals: 18,
      isNative: true,
      symbol: "ETH",
      address: "0x",
      enabled: true,
    };
    const result = convertToWei("0.000000000000000001", token);
    expect(result).toBe("1");
  });

  it("should handle very large amounts", () => {
    const token = {
      decimals: 18,
      isNative: true,
      symbol: "ETH",
      address: "0x",
      enabled: true,
    };
    const result = convertToWei("1000000", token);
    expect(result).toBe("1000000000000000000000000");
  });
});

describe("isXChainSwapContent", () => {
  it("should return true for valid content", () => {
    const content = {
      fromChain: "ethereum",
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(true);
  });

  it("should accept number amount", () => {
    const content = {
      fromChain: "ethereum",
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      amount: 1,
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(true);
  });

  it("should return false for missing fromChain", () => {
    const content = {
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for missing toChain", () => {
    const content = {
      fromChain: "ethereum",
      fromToken: "ETH",
      toToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for missing fromToken", () => {
    const content = {
      fromChain: "ethereum",
      toChain: "base",
      toToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for missing toToken", () => {
    const content = {
      fromChain: "ethereum",
      toChain: "base",
      fromToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for missing amount", () => {
    const content = {
      fromChain: "ethereum",
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for missing toAddress", () => {
    const content = {
      fromChain: "ethereum",
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      amount: "1",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for wrong type fields", () => {
    const content = {
      fromChain: 123,
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });

  it("should return false for null values", () => {
    const content = {
      fromChain: null,
      toChain: "base",
      fromToken: "ETH",
      toToken: "ETH",
      amount: "1",
      toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
    };

    expect(isXChainSwapContent(content as any)).toBe(false);
  });
});

describe("validateSquidRouterConfig", () => {
  let mockRuntime: any;

  beforeEach(() => {
    mockRuntime = createMockRuntime();
  });

  it("should validate valid configuration", async () => {
    const config = await validateSquidRouterConfig(mockRuntime);

    expect(config).toBeDefined();
    expect(config.SQUID_INTEGRATOR_ID).toBe("test-integrator-id");
    expect(config.SQUID_SDK_URL).toBe("https://apiplus.squidrouter.com");
    expect(config.SQUID_EVM_ADDRESS).toBe(
      "0x742D35Cc6634c0532925A3b844BC9E7595f6F269"
    );
    expect(config.SQUID_EVM_PRIVATE_KEY).toBe(
      "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    );
  });

  it("should throw error for missing integrator ID", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_INTEGRATOR_ID") return undefined;
      return createMockRuntime().getSetting(key);
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "Required"
    );
  });

  it("should throw error for missing SDK URL", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_SDK_URL") return undefined;
      return createMockRuntime().getSetting(key);
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "Required"
    );
  });

  it("should throw error for invalid EVM address", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_EVM_ADDRESS") return "invalid-address";
      return createMockRuntime().getSetting(key);
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "EVM_ADDRESS is invalid"
    );
  });

  it("should throw error for invalid EVM private key", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_EVM_PRIVATE_KEY") return "invalid-key";
      return createMockRuntime().getSetting(key);
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "64-character hexadecimal string"
    );
  });

  it("should accept private key with 0x prefix", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_EVM_PRIVATE_KEY") {
        return "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
      }
      return createMockRuntime().getSetting(key);
    });

    const config = await validateSquidRouterConfig(mockRuntime);
    expect(config.SQUID_EVM_PRIVATE_KEY).toBeDefined();
  });

  it("should accept private key without 0x prefix", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_EVM_PRIVATE_KEY") {
        return "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
      }
      return createMockRuntime().getSetting(key);
    });

    const config = await validateSquidRouterConfig(mockRuntime);
    expect(config.SQUID_EVM_PRIVATE_KEY).toBeDefined();
  });

  it("should throw error when EVM address is provided without private key", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_EVM_PRIVATE_KEY") return undefined;
      return createMockRuntime().getSetting(key);
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "SQUID_EVM_PRIVATE_KEY: Required"
    );
  });

  it("should throw error when EVM private key is provided without address", async () => {
    mockRuntime.getSetting.mockImplementation((key: string) => {
      if (key === "SQUID_EVM_ADDRESS") return undefined;
      return createMockRuntime().getSetting(key);
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "SQUID_EVM_ADDRESS: Required"
    );
  });

  it("should handle non-Zod errors", async () => {
    mockRuntime.getSetting.mockImplementation(() => {
      throw new Error("Unexpected error");
    });

    await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
      "Unexpected error"
    );
  });
});

describe("squidRouterEnvSchema", () => {
  it("should accept valid configuration", () => {
    const config = {
      SQUID_INTEGRATOR_ID: "test-id",
      SQUID_SDK_URL: "https://api.squid.com",
      SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
      SQUID_EVM_PRIVATE_KEY:
        "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
    };

    const result = squidRouterEnvSchema.parse(config);
    expect(result).toEqual(config);
  });

  it("should reject empty integrator ID", () => {
    const config = {
      SQUID_INTEGRATOR_ID: "",
      SQUID_SDK_URL: "https://api.squid.com",
      SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
      SQUID_EVM_PRIVATE_KEY:
        "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
    };

    expect(() => squidRouterEnvSchema.parse(config)).toThrow();
  });

  it("should validate EVM address checksum", () => {
    const config = {
      SQUID_INTEGRATOR_ID: "test-id",
      SQUID_SDK_URL: "https://api.squid.com",
      SQUID_EVM_ADDRESS: "0x742d35cc6634c0532925a3b844bc9e7595f62999", // lowercase
      SQUID_EVM_PRIVATE_KEY:
        "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
    };

    // ethers.isAddress accepts both checksummed and non-checksummed addresses
    const result = squidRouterEnvSchema.parse(config);
    expect(result).toBeDefined();
  });

  it("should validate private key length", () => {
    const config = {
      SQUID_INTEGRATOR_ID: "test-id",
      SQUID_SDK_URL: "https://api.squid.com",
      SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
      SQUID_EVM_PRIVATE_KEY: "0x123", // too short
    };

    expect(() => squidRouterEnvSchema.parse(config)).toThrow(
      "64-character hexadecimal"
    );
  });

  it("should validate private key contains only hex characters", () => {
    const config = {
      SQUID_INTEGRATOR_ID: "test-id",
      SQUID_SDK_URL: "https://api.squid.com",
      SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
      SQUID_EVM_PRIVATE_KEY:
        "0xGGGG456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", // invalid hex
    };

    expect(() => squidRouterEnvSchema.parse(config)).toThrow(
      "64-character hexadecimal"
    );
  });
});
