import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { PropertyScannerService } from '../src/scanner.service';
import { prisma } from '@shieldai/db';

vi.mock('@shieldai/db', () => ({
  prisma: {
    countyDeedRecord: {
      findMany: vi.fn(),
    },
  },
}));

describe('PropertyScannerService', () => {
  let scanner: PropertyScannerService;

  beforeEach(() => {
    vi.clearAllMocks();
    scanner = new PropertyScannerService({
      attomApiKey: 'test-api-key',
      uspsApiKey: 'usps-test-key',
      countyScraperEnabled: true,
      cacheTTL: 60,
      rateLimitPoints: 100,
      rateLimitDuration: 60,
      retryAttempts: 2,
      retryDelayMs: 100,
    });
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('fetchByAddress', () => {
    it('should fetch property data from ATTOM API by address', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-123',
            parcelNumber: '123-456-789',
            address: {
              streetAddress: '123 Main St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'John Doe',
              ownerName2: 'Jane Doe',
            },
            assessment: {
              totalValue: 1000000,
              landValue: 400000,
              improvementValue: 600000,
              taxYear: 2024,
              taxAmount: 12000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1990,
              squareFeet: 2500,
              bedrooms: 4,
              bathrooms: 3,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValueOnce([]);

      const result = await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102');

      expect(result).not.toBeNull();
      expect(result?.parcelNumber).toBe('123-456-789');
      expect(result?.ownerName).toBe('John Doe');
      expect(result?.address.city).toBe('San Francisco');
      expect(result?.dataSource).toBe('attom');
      expect(result?.assessment?.totalValue).toBe(1000000);
    });

    it('should return null when property not found', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: false,
        status: 404,
        statusText: 'Not Found',
      });

      const result = await scanner.fetchByAddress('999 Nonexistent St');

      expect(result).toBeNull();
    });

    it('should combine ATTOM data with county deed records', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-123',
            parcelNumber: '123-456-789',
            address: {
              streetAddress: '123 Main St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'John Doe',
            },
            assessment: {
              totalValue: 1000000,
              landValue: 400000,
              improvementValue: 600000,
              taxYear: 2024,
              taxAmount: 12000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1990,
              squareFeet: 2500,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValueOnce([
        {
          id: 'deed-1',
          documentId: 'DOC-2024-001',
          recordingDate: new Date('2024-01-15'),
          documentType: 'WARRANTY DEED',
          grantorName: 'Previous Owner LLC',
          granteeName: 'John Doe',
          propertyAddress: '123 Main St',
          parcelNumber: '123-456-789',
          considerationAmount: 950000,
        },
      ]);

      const result = await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102', 'San Francisco');

      expect(result).not.toBeNull();
      expect(result?.dataSource).toBe('combined');
      expect(result?.deedHistory?.length).toBe(1);
      expect(result?.deedHistory?.[0].documentType).toBe('WARRANTY DEED');
    });

    it('should handle ATTOM API errors with retry', async () => {
      global.fetch = vi.fn()
        .mockRejectedValueOnce(new Error('Network error'))
        .mockRejectedValueOnce(new Error('Network error'))
        .mockResolvedValueOnce({
          ok: true,
          status: 200,
          json: async () => ({
            data: {
              propertyId: 'prop-123',
              parcelNumber: '123-456-789',
              address: {
                streetAddress: '123 Main St',
                city: 'San Francisco',
                state: 'CA',
                zipCode: '94102',
                latitude: 37.7749,
                longitude: -122.4194,
              },
              owner: {
                ownerName1: 'John Doe',
              },
              assessment: {
                totalValue: 1000000,
                landValue: 400000,
                improvementValue: 600000,
                taxYear: 2024,
                taxAmount: 12000,
              },
              propertyDetails: {
                propertyType: 'residential',
                yearBuilt: 1990,
                squareFeet: 2500,
              },
              salesHistory: [],
            },
          }),
        });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValueOnce([]);

      const result = await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102');

      expect(result).not.toBeNull();
      expect(result?.ownerName).toBe('John Doe');
      expect(global.fetch).toHaveBeenCalledTimes(3);
    });
  });

  describe('fetchByParcel', () => {
    it('should fetch property data by parcel number', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-456',
            parcelNumber: '987-654-321',
            address: {
              streetAddress: '456 Oak Ave',
              city: 'Los Angeles',
              state: 'CA',
              zipCode: '90001',
              latitude: 34.0522,
              longitude: -118.2437,
            },
            owner: {
              ownerName1: 'Alice Smith',
            },
            assessment: {
              totalValue: 750000,
              landValue: 300000,
              improvementValue: 450000,
              taxYear: 2024,
              taxAmount: 9000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 2000,
              squareFeet: 1800,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValueOnce([]);

      const result = await scanner.fetchByParcel('987-654-321', 'Los Angeles');

      expect(result).not.toBeNull();
      expect(result?.parcelNumber).toBe('987-654-321');
      expect(result?.ownerName).toBe('Alice Smith');
      expect(result?.address.city).toBe('Los Angeles');
    });

    it('should fetch county liens for parcel', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-456',
            parcelNumber: '987-654-321',
            address: {
              streetAddress: '456 Oak Ave',
              city: 'Los Angeles',
              state: 'CA',
              zipCode: '90001',
              latitude: 34.0522,
              longitude: -118.2437,
            },
            owner: {
              ownerName1: 'Alice Smith',
            },
            assessment: {
              totalValue: 750000,
              landValue: 300000,
              improvementValue: 450000,
              taxYear: 2024,
              taxAmount: 9000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 2000,
              squareFeet: 1800,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany)
        .mockResolvedValueOnce([])
        .mockResolvedValueOnce([
          {
            id: 'lien-1',
            documentId: 'LIEN-2024-001',
            recordingDate: new Date('2024-02-01'),
            documentType: 'MECHANIC LIEN',
            grantorName: 'ABC Construction',
            granteeName: 'Alice Smith',
            propertyAddress: '456 Oak Ave',
            parcelNumber: '987-654-321',
            considerationAmount: 15000,
          },
        ]);

      const result = await scanner.fetchByParcel('987-654-321', 'Los Angeles');

      expect(result).not.toBeNull();
      expect(result?.liens?.length).toBe(1);
      expect(result?.liens?.[0].lienType).toBe('MECHANIC LIEN');
      expect(result?.liens?.[0].lienAmount).toBe(15000);
    });
  });

  describe('standardizeAddress', () => {
    it('should standardize address using USPS API', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          streetNumber: '123',
          streetName: 'Main',
          streetSuffix: 'St',
          unitType: 'Apt',
          unitValue: '4B',
          city: 'San Francisco',
          state: 'CA',
          zip5: '94102',
          zip4: '1234',
          deliveryPointBarCode: '941021234123',
          isDeliverable: true,
        }),
      });

      const result = await scanner.standardizeAddress('123 main street apt 4b san francisco ca 94102');

      expect(result).not.toBeNull();
      expect(result?.streetNumber).toBe('123');
      expect(result?.streetName).toBe('Main');
      expect(result?.city).toBe('San Francisco');
      expect(result?.isDeliverable).toBe(true);
    });

    it('should return null for undeliverable address', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          streetNumber: '999',
          streetName: 'Nonexistent',
          streetSuffix: 'St',
          city: 'Nowhere',
          state: 'XX',
          zip5: '00000',
          isDeliverable: false,
        }),
      });

      const result = await scanner.standardizeAddress('999 nonexistent st nowhere xx 00000');

      expect(result).not.toBeNull();
      expect(result?.isDeliverable).toBe(false);
    });
  });

  describe('batchFetchProperties', () => {
    it('should fetch multiple properties in batch', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-1',
            parcelNumber: '111-222-333',
            address: {
              streetAddress: '111 First St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'Owner One',
            },
            assessment: {
              totalValue: 500000,
              landValue: 200000,
              improvementValue: 300000,
              taxYear: 2024,
              taxAmount: 6000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1985,
              squareFeet: 1500,
            },
            salesHistory: [],
          },
        }),
      }).mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-2',
            parcelNumber: '444-555-666',
            address: {
              streetAddress: '222 Second Ave',
              city: 'Los Angeles',
              state: 'CA',
              zipCode: '90001',
              latitude: 34.0522,
              longitude: -118.2437,
            },
            owner: {
              ownerName1: 'Owner Two',
            },
            assessment: {
              totalValue: 800000,
              landValue: 350000,
              improvementValue: 450000,
              taxYear: 2024,
              taxAmount: 9600,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1995,
              squareFeet: 2000,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValue([]);

      const requests = [
        { address: '111 First St, San Francisco, CA 94102' },
        { address: '222 Second Ave, Los Angeles, CA 90001' },
      ];

      const results = await scanner.batchFetchProperties(requests);

      expect(results.length).toBe(2);
      expect(results[0]).not.toBeNull();
      expect(results[1]).not.toBeNull();
      expect(results[0]?.ownerName).toBe('Owner One');
      expect(results[1]?.ownerName).toBe('Owner Two');
    });

    it('should handle batch with mixed valid and invalid requests', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-1',
            parcelNumber: '111-222-333',
            address: {
              streetAddress: '111 First St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'Owner One',
            },
            assessment: {
              totalValue: 500000,
              landValue: 200000,
              improvementValue: 300000,
              taxYear: 2024,
              taxAmount: 6000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1985,
              squareFeet: 1500,
            },
            salesHistory: [],
          },
        }),
      }).mockResolvedValueOnce({
        ok: false,
        status: 404,
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValue([]);

      const requests = [
        { address: '111 First St, San Francisco, CA 94102' },
        { address: '999 Invalid Address' },
      ];

      const results = await scanner.batchFetchProperties(requests);

      expect(results.length).toBe(2);
      expect(results[0]).not.toBeNull();
      expect(results[1]).toBeNull();
    });
  });

  describe('cache behavior', () => {
    it('should use cached results for repeated requests', async () => {
      global.fetch = vi.fn().mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-123',
            parcelNumber: '123-456-789',
            address: {
              streetAddress: '123 Main St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'John Doe',
            },
            assessment: {
              totalValue: 1000000,
              landValue: 400000,
              improvementValue: 600000,
              taxYear: 2024,
              taxAmount: 12000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1990,
              squareFeet: 2500,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValueOnce([]);

      await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102');
      await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102');

      expect(global.fetch).toHaveBeenCalledTimes(1);
    });

    it('should invalidate cache when requested', async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-123',
            parcelNumber: '123-456-789',
            address: {
              streetAddress: '123 Main St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'John Doe',
            },
            assessment: {
              totalValue: 1000000,
              landValue: 400000,
              improvementValue: 600000,
              taxYear: 2024,
              taxAmount: 12000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1990,
              squareFeet: 2500,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValue([]);

      await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102');
      await scanner.invalidateCache('123 Main St, San Francisco, CA 94102');
      await scanner.fetchByAddress('123 Main St, San Francisco, CA 94102');

      expect(global.fetch).toHaveBeenCalledTimes(2);
    });
  });

  describe('rate limiting', () => {
    it('should handle rate limit errors gracefully', async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        status: 200,
        json: async () => ({
          data: {
            propertyId: 'prop-123',
            parcelNumber: '123-456-789',
            address: {
              streetAddress: '123 Main St',
              city: 'San Francisco',
              state: 'CA',
              zipCode: '94102',
              latitude: 37.7749,
              longitude: -122.4194,
            },
            owner: {
              ownerName1: 'John Doe',
            },
            assessment: {
              totalValue: 1000000,
              landValue: 400000,
              improvementValue: 600000,
              taxYear: 2024,
              taxAmount: 12000,
            },
            propertyDetails: {
              propertyType: 'residential',
              yearBuilt: 1990,
              squareFeet: 2500,
            },
            salesHistory: [],
          },
        }),
      });

      vi.mocked(prisma.countyDeedRecord.findMany).mockResolvedValue([]);

      const scannerWithStrictLimit = new PropertyScannerService({
        attomApiKey: 'test-api-key',
        countyScraperEnabled: false,
        rateLimitPoints: 1,
        rateLimitDuration: 60,
      });

      await scannerWithStrictLimit.fetchByAddress('123 Main St, San Francisco, CA 94102');

      await expect(
        scannerWithStrictLimit.fetchByAddress('456 Oak Ave, San Francisco, CA 94102')
      ).rejects.toThrow('Rate limit exceeded');
    });
  });
});
