import { getInitialURL } from "./helpers";

describe("wallet-api helpers", () => {
  describe("isWhitelistedDomain (via getInitialURL)", () => {
    const mockManifest = {
      url: "https://default.example.com/",
      domains: ["ledger.com", "*.subdomain.ledger.com", "approved.io"],
    };

    describe("malicious URL bypass attempts", () => {
      it("should reject URL with whitelisted domain in query parameter", () => {
        const inputs = {
          goToURL: "https://evil.example/?next=ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        // Should fall back to manifest.url, not use goToURL
        expect(result).toBe(mockManifest.url);
      });

      it("should reject URL with whitelisted domain in hash fragment", () => {
        const inputs = {
          goToURL: "https://evil.example/#ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject URL with whitelisted domain in path", () => {
        const inputs = {
          goToURL: "https://evil.example/ledger.com/redirect",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject URL with whitelisted domain as subdomain of malicious domain", () => {
        const inputs = {
          goToURL: "https://ledger.com.evil.example",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject URL with similar but different domain", () => {
        const inputs = {
          goToURL: "https://ledger.com.fake.io",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });
    });

    describe("scheme validation", () => {
      it("should reject HTTP URLs", () => {
        const inputs = {
          goToURL: "http://ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject javascript: URLs", () => {
        const inputs = {
          goToURL: "javascript:alert(1)",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject data: URLs", () => {
        const inputs = {
          goToURL: "data:text/html,<script>alert(1)</script>",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject file: URLs", () => {
        const inputs = {
          goToURL: "file:///etc/passwd",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject ftp: URLs", () => {
        const inputs = {
          goToURL: "ftp://ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });
    });

    describe("legitimate URLs", () => {
      it("should reject URL not on same domain as manifest", () => {
        const inputs = {
          goToURL: "https://ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject URL with different domain even with path", () => {
        const inputs = {
          goToURL: "https://ledger.com/page?param=value",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject URL on different domain", () => {
        const inputs = {
          goToURL: "https://approved.io/some/path",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should allow URL on same domain as manifest URL", () => {
        const inputs = {
          goToURL: "https://default.example.com/different/path",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(inputs.goToURL);
      });

      it("should allow URL on same domain as manifest URL with different path and params", () => {
        const inputs = {
          goToURL: "https://default.example.com/page?test=value",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(inputs.goToURL);
      });

      it("should be case-insensitive for domain matching", () => {
        const inputs = {
          goToURL: "https://DEFAULT.EXAMPLE.COM/path",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(inputs.goToURL);
      });

      it("should allow URL on same domain with different case", () => {
        const manifestWithUppercase = {
          url: "https://default.example.com/",
          domains: ["LEDGER.COM"],
        };
        const inputs = {
          goToURL: "https://DEFAULT.EXAMPLE.COM/page",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, manifestWithUppercase);
        expect(result).toBe(inputs.goToURL);
      });
    });

    describe("wildcard pattern matching", () => {
      it("should reject subdomain not matching manifest domain", () => {
        const inputs = {
          goToURL: "https://test.subdomain.ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject nested subdomain not matching manifest domain", () => {
        const inputs = {
          goToURL: "https://a.b.subdomain.ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject domain not matching manifest domain", () => {
        const inputs = {
          goToURL: "https://subdomain.ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject subdomain not matching wildcard pattern", () => {
        const inputs = {
          goToURL: "https://test.wrong.ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject when wildcard pattern should not match", () => {
        const inputs = {
          goToURL: "https://notsubdomain.ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });
    });

    describe("internationalized domain names", () => {
      it("should handle IDN domains (punycode) on same domain", () => {
        const manifestWithIDN = {
          url: "https://münchen.de/",
          domains: ["münchen.de"],
        };
        const inputs = {
          goToURL: "https://xn--mnchen-3ya.de/page", // punycode for münchen.de
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, manifestWithIDN);
        expect(result).toBe(inputs.goToURL);
      });

      it("should handle IDN domains in unicode form on same domain", () => {
        const manifestWithIDN = {
          url: "https://xn--mnchen-3ya.de/",
          domains: ["münchen.de"],
        };
        const inputs = {
          goToURL: "https://münchen.de/page",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, manifestWithIDN);
        expect(result).toBe(inputs.goToURL);
      });
    });

    describe("edge cases", () => {
      it("should reject malformed URLs", () => {
        const inputs = {
          goToURL: "not-a-valid-url",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should reject empty URL", () => {
        const inputs = {
          goToURL: "",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should handle URL with port on same domain", () => {
        const inputs = {
          goToURL: "https://default.example.com:8080/path",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(inputs.goToURL);
      });

      it("should handle URL with authentication on same domain", () => {
        const inputs = {
          goToURL: "https://user:pass@default.example.com/path",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(inputs.goToURL);
      });

      it("should work when goToURL is not provided", () => {
        const inputs = {};
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should work when inputs is undefined", () => {
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(undefined, mockManifest);
        expect(result).toBe(mockManifest.url);
      });

      it("should handle URL with only scheme separator", () => {
        const inputs = {
          goToURL: "https://",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
      });
    });

    describe("deeplink attack scenario", () => {
      it("should block deeplink with malicious goToURL", () => {
        // Simulates: ledgerlive://discover/<appId>?goToURL=https://evil.example/?next=ledger.com
        const inputs = {
          goToURL: "https://evil.example/?next=ledger.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(mockManifest.url);
        expect(result).not.toContain("evil.example");
      });

      it("should allow deeplink with goToURL on same domain", () => {
        // Simulates: ledgerlive://discover/<appId>?goToURL=https://default.example.com/page
        const inputs = {
          goToURL: "https://default.example.com/page",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, mockManifest);
        expect(result).toBe(inputs.goToURL);
        expect(result).toContain("default.example.com");
      });
    });

    describe("real-world manifest patterns", () => {
      it('should handle "https://" pattern (matches nothing with strict hostname validation)', () => {
        const manifestWithGenericPattern = {
          url: "https://default.example.com/",
          domains: ["https://"], // This pattern doesn't make sense with hostname validation
        };
        const inputs = {
          goToURL: "https://any.domain.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, manifestWithGenericPattern);
        // Should reject because "https://" is not a valid hostname pattern
        expect(result).toBe(manifestWithGenericPattern.url);
      });

      it('should reject "*" pattern with non-HTTPS scheme', () => {
        const manifestWithHttpPattern = {
          url: "https://default.example.com/",
          domains: ["*"], // "*" is not a valid pattern
        };
        const inputs = {
          goToURL: "http://any.domain.com", // HTTP should be rejected
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, manifestWithHttpPattern);
        expect(result).toBe(manifestWithHttpPattern.url);
      });

      it('should reject "*" wildcard (all domains not allowed)', () => {
        const manifestWithWildcard = {
          url: "https://default.example.com/",
          domains: ["*"],
        };
        const inputs = {
          goToURL: "https://any.domain.com",
        };
        // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
        const result = getInitialURL(inputs, manifestWithWildcard);
        // "*" alone should not match any domain for security reasons
        expect(result).toBe(manifestWithWildcard.url);
      });
    });
  });

  describe("getInitialURL with manifest params", () => {
    it("should append params when goToURL is not provided", () => {
      const manifest = {
        url: "https://example.com",
        domains: ["example.com"],
        params: { foo: "bar" },
      };
      const inputs = { customParam: "value" };
      // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
      const result = getInitialURL(inputs, manifest);

      expect(result).toContain("example.com");
      expect(result).toContain("customParam=value");
      expect(result).toContain("params=%7B%22foo%22%3A%22bar%22%7D"); // JSON.stringify({foo: "bar"}) encoded
    });

    it("should use goToURL as-is when on same domain (without adding params)", () => {
      const manifest = {
        url: "https://example.com",
        domains: ["allowed.com"],
        params: { foo: "bar" },
      };
      const inputs = {
        goToURL: "https://example.com/page",
        customParam: "value",
      };
      // @ts-expect-error - test mock object doesn't have all LiveAppManifest properties
      const result = getInitialURL(inputs, manifest);

      expect(result).toBe("https://example.com/page");
      expect(result).not.toContain("customParam");
      expect(result).not.toContain("params");
    });
  });
});
