import { describe, it, expect, vi, beforeEach } from "vitest";
import {
  generateFingerprint,
  calculateFingerprintSimilarity,
  calculateGeoDistance,
  isImpossibleTravel,
  DeviceSignature,
  LocationData,
} from "./deviceFingerprinting.ts";

describe("Device Fingerprinting Service", () => {
  describe("generateFingerprint", () => {
    it("should generate consistent fingerprint for same signature", () => {
      const signature: Partial<DeviceSignature> = {
        userAgent: "Mozilla/5.0",
        platform: "MacIntel",
        screenResolution: "1920x1080",
      };

      const fp1 = generateFingerprint(signature);
      const fp2 = generateFingerprint(signature);

      expect(fp1).toBe(fp2);
      expect(fp1).toMatch(/^[a-f0-9]{64}$/); // SHA256 hex
    });

    it("should generate different fingerprints for different signatures", () => {
      const sig1: Partial<DeviceSignature> = { userAgent: "Chrome" };
      const sig2: Partial<DeviceSignature> = { userAgent: "Firefox" };

      const fp1 = generateFingerprint(sig1);
      const fp2 = generateFingerprint(sig2);

      expect(fp1).not.toBe(fp2);
    });

    it("should handle empty signature", () => {
      const fp = generateFingerprint({});
      expect(fp).toMatch(/^[a-f0-9]{64}$/);
    });
  });

  describe("calculateFingerprintSimilarity", () => {
    it("should return 1 for identical signatures", () => {
      const sig: Partial<DeviceSignature> = {
        userAgent: "Chrome",
        platform: "Win32",
        screenResolution: "1920x1080",
      };

      const similarity = calculateFingerprintSimilarity(sig, sig);
      expect(similarity).toBe(1);
    });

    it("should return 0 for completely different signatures", () => {
      const sig1: Partial<DeviceSignature> = {
        userAgent: "Chrome",
        platform: "Win32",
      };
      const sig2: Partial<DeviceSignature> = {
        userAgent: "Firefox",
        platform: "MacIntel",
      };

      const similarity = calculateFingerprintSimilarity(sig1, sig2);
      expect(similarity).toBe(0);
    });

    it("should return partial similarity for partially matching signatures", () => {
      const sig1: Partial<DeviceSignature> = {
        userAgent: "Chrome",
        platform: "Win32",
        screenResolution: "1920x1080",
      };
      const sig2: Partial<DeviceSignature> = {
        userAgent: "Chrome",
        platform: "MacIntel",
        screenResolution: "1920x1080",
      };

      const similarity = calculateFingerprintSimilarity(sig1, sig2);
      expect(similarity).toBeGreaterThan(0);
      expect(similarity).toBeLessThan(1);
    });

    it("should return 0 for empty signatures", () => {
      const similarity = calculateFingerprintSimilarity({}, {});
      expect(similarity).toBe(0);
    });
  });

  describe("calculateGeoDistance", () => {
    it("should return 0 for same location", () => {
      const distance = calculateGeoDistance(37.7749, -122.4194, 37.7749, -122.4194);
      expect(distance).toBeLessThan(0.1);
    });

    it("should calculate distance between two cities", () => {
      // San Francisco to Los Angeles (approximately 559 km)
      const distance = calculateGeoDistance(37.7749, -122.4194, 34.0522, -118.2437);
      expect(distance).toBeGreaterThan(500);
      expect(distance).toBeLessThan(600);
    });

    it("should calculate distance between continents", () => {
      // New York to London (approximately 5585 km)
      const distance = calculateGeoDistance(40.7128, -74.006, 51.5074, -0.1278);
      expect(distance).toBeGreaterThan(5000);
      expect(distance).toBeLessThan(6000);
    });
  });

  describe("isImpossibleTravel", () => {
    it("should detect impossible travel (too fast)", () => {
      const prevLocation: LocationData = {
        country: "US",
        city: "San Francisco",
        latitude: 37.7749,
        longitude: -122.4194,
        timezone: "America/Los_Angeles",
        isp: "ISP",
        isVPN: false,
        isProxy: false,
      };

      const currentLocation: LocationData = {
        country: "GB",
        city: "London",
        latitude: 51.5074,
        longitude: -0.1278,
        timezone: "Europe/London",
        isp: "ISP",
        isVPN: false,
        isProxy: false,
      };

      // 5585 km in 30 minutes = impossible
      const isImpossible = isImpossibleTravel(prevLocation, currentLocation, 30);
      expect(isImpossible).toBe(true);
    });

    it("should allow realistic travel", () => {
      const prevLocation: LocationData = {
        country: "US",
        city: "San Francisco",
        latitude: 37.7749,
        longitude: -122.4194,
        timezone: "America/Los_Angeles",
        isp: "ISP",
        isVPN: false,
        isProxy: false,
      };

      const currentLocation: LocationData = {
        country: "US",
        city: "Los Angeles",
        latitude: 34.0522,
        longitude: -118.2437,
        timezone: "America/Los_Angeles",
        isp: "ISP",
        isVPN: false,
        isProxy: false,
      };

      // 559 km in 8 hours = realistic (70 km/h average)
      const isImpossible = isImpossibleTravel(prevLocation, currentLocation, 480);
      expect(isImpossible).toBe(false);
    });

    it("should not flag short distance travel as impossible", () => {
      const prevLocation: LocationData = {
        country: "US",
        city: "San Francisco",
        latitude: 37.7749,
        longitude: -122.4194,
        timezone: "America/Los_Angeles",
        isp: "ISP",
        isVPN: false,
        isProxy: false,
      };

      const currentLocation: LocationData = {
        country: "US",
        city: "Oakland",
        latitude: 37.8044,
        longitude: -122.2712,
        timezone: "America/Los_Angeles",
        isp: "ISP",
        isVPN: false,
        isProxy: false,
      };

      // ~20 km distance - should not be flagged even in 1 minute
      const isImpossible = isImpossibleTravel(prevLocation, currentLocation, 1);
      expect(isImpossible).toBe(false);
    });
  });

  describe("Fingerprint edge cases", () => {
    it("should handle null/undefined values in signature", () => {
      const sig: Partial<DeviceSignature> = {
        userAgent: "Chrome",
        platform: undefined,
        screenResolution: null as any,
      };

      const fp = generateFingerprint(sig);
      expect(fp).toMatch(/^[a-f0-9]{64}$/);
    });

    it("should generate different fingerprints for case-sensitive data", () => {
      const sig1 = { userAgent: "Chrome" };
      const sig2 = { userAgent: "chrome" };

      const fp1 = generateFingerprint(sig1);
      const fp2 = generateFingerprint(sig2);

      expect(fp1).not.toBe(fp2);
    });
  });

  describe("Geographic distance edge cases", () => {
    it("should handle antipodal points", () => {
      // North Pole to South Pole
      const distance = calculateGeoDistance(90, 0, -90, 0);
      expect(distance).toBeCloseTo(20015, -1); // Half Earth circumference
    });

    it("should handle international date line", () => {
      // East side to west side of date line
      const distance = calculateGeoDistance(0, 179.9, 0, -179.9);
      expect(distance).toBeLessThan(50); // Very close
    });
  });
});
