import { ComponentFixture, TestBed } from '@angular/core/testing';
import { signal, ResourceRef } from '@angular/core';
import { vi } from 'vitest';
import { CountryResource } from '@isa/crm/data-access';
import { AddressComponent } from './address.component';
import { Address } from './types';

describe('AddressComponent', () => {
  let component: AddressComponent;
  let fixture: ComponentFixture<AddressComponent>;
  let mockCountryResource: CountryResource;

  beforeEach(async () => {
    const mockResourceRef = {
      value: vi.fn().mockReturnValue([
        { isO3166_A_3: 'FRA', name: 'France' },
        { isO3166_A_3: 'AUT', name: 'Austria' },
        { isO3166_A_3: 'USA', name: 'United States' },
      ]),
      isLoading: vi.fn().mockReturnValue(false),
      hasValue: vi.fn().mockReturnValue(true),
      error: vi.fn().mockReturnValue(null),
      status: vi.fn().mockReturnValue('Resolved'),
      reload: vi.fn(),
    } as unknown as ResourceRef<any>;

    mockCountryResource = {
      resource: mockResourceRef,
    } as CountryResource;

    await TestBed.configureTestingModule({
      imports: [AddressComponent],
      providers: [{ provide: CountryResource, useValue: mockCountryResource }],
    }).compileComponents();

    fixture = TestBed.createComponent(AddressComponent);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  describe('Complete address display', () => {
    it('should display all address fields including info, careOf, and apartment', () => {
      // Arrange
      const address: Address = {
        careOf: 'John Doe',
        street: 'Main Street',
        streetNumber: '42',
        apartment: 'Apt 3B',
        info: 'Building A, 3rd Floor',
        zipCode: '12345',
        city: 'Berlin',
        country: 'DEU',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const careOfLine = compiled.querySelector('[data-which="care-of-line"]');
      const streetLine = compiled.querySelector('[data-which="street-line"]');
      const info = compiled.querySelector('[data-which="info"]');
      const cityLine = compiled.querySelector('[data-which="city-line"]');

      expect(careOfLine?.textContent?.trim()).toBe('c/o John Doe');
      expect(streetLine?.textContent?.trim()).toBe('Main Street 42 Apt 3B');
      expect(info?.textContent?.trim()).toBe('Building A, 3rd Floor');
      expect(cityLine?.textContent?.trim()).toBe('12345 Berlin');
    });
  });

  describe('German address handling', () => {
    it('should not display country for DEU', () => {
      // Arrange
      const address: Address = {
        street: 'Hauptstraße',
        streetNumber: '10',
        zipCode: '80331',
        city: 'München',
        country: 'DEU',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      expect(cityLine?.textContent?.trim()).toBe('80331 München');
      expect(cityLine?.textContent).not.toContain('DEU');
    });

    it('should display country when country name is "Germany" (not code DEU)', () => {
      // Arrange
      const address: Address = {
        street: 'Berliner Allee',
        streetNumber: '5',
        zipCode: '40212',
        city: 'Düsseldorf',
        country: 'Germany',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      expect(cityLine?.textContent?.trim()).toBe('40212 Düsseldorf, Germany');
    });
  });

  describe('Non-German address handling', () => {
    it('should display country name from country code for non-German addresses', () => {
      // Arrange
      const address: Address = {
        street: 'Rue de la Paix',
        streetNumber: '15',
        zipCode: '75002',
        city: 'Paris',
        country: 'FRA',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      expect(cityLine?.textContent?.trim()).toBe('75002 Paris, France');
    });

    it('should display country name for Austrian addresses', () => {
      // Arrange
      const address: Address = {
        street: 'Stephansplatz',
        streetNumber: '1',
        zipCode: '1010',
        city: 'Wien',
        country: 'AUT',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      expect(cityLine?.textContent?.trim()).toBe('1010 Wien, Austria');
    });

    it('should fallback to country code when country name not found', () => {
      // Arrange
      const address: Address = {
        street: 'Unknown Street',
        streetNumber: '1',
        zipCode: '12345',
        city: 'Unknown City',
        country: 'XYZ',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      expect(cityLine?.textContent?.trim()).toBe('12345 Unknown City, XYZ');
    });
  });

  describe('Care of line', () => {
    it('should display care of line with c/o prefix', () => {
      // Arrange
      const address: Address = {
        careOf: 'Jane Smith',
        street: 'Test Street',
        streetNumber: '1',
        zipCode: '12345',
        city: 'Test City',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const careOfLine = compiled.querySelector('[data-which="care-of-line"]');
      expect(careOfLine?.textContent?.trim()).toBe('c/o Jane Smith');
    });

    it('should not render care of line when field is empty', () => {
      // Arrange
      const address: Address = {
        street: 'Test Street',
        streetNumber: '1',
        zipCode: '12345',
        city: 'Test City',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const careOfLine = compiled.querySelector('[data-which="care-of-line"]');
      expect(careOfLine).toBeNull();
    });
  });

  describe('Apartment handling', () => {
    it('should include apartment in street line', () => {
      // Arrange
      const address: Address = {
        street: 'Main Street',
        streetNumber: '10',
        apartment: 'Unit 5A',
        zipCode: '12345',
        city: 'Test City',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const streetLine = compiled.querySelector('[data-which="street-line"]');
      expect(streetLine?.textContent?.trim()).toBe('Main Street 10 Unit 5A');
    });
  });

  describe('Address without info', () => {
    it('should not render info line when field is empty', () => {
      // Arrange
      const address: Address = {
        street: 'Test Street',
        streetNumber: '1',
        zipCode: '12345',
        city: 'Test City',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const info = compiled.querySelector('[data-which="info"]');
      expect(info).toBeNull();
    });
  });

  describe('Partial addresses', () => {
    it('should handle address with only street', () => {
      // Arrange
      const address: Address = {
        street: 'Incomplete Street',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const streetLine = compiled.querySelector('[data-which="street-line"]');
      expect(streetLine?.textContent?.trim()).toBe('Incomplete Street');
    });

    it('should handle address with only city information', () => {
      // Arrange
      const address: Address = {
        zipCode: '12345',
        city: 'City Only',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      expect(cityLine?.textContent?.trim()).toBe('12345 City Only');
    });

    it('should not render lines when fields are missing', () => {
      // Arrange
      const address: Address = {
        info: 'Only info',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const streetLine = compiled.querySelector('[data-which="street-line"]');
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      const info = compiled.querySelector('[data-which="info"]');

      expect(streetLine).toBeNull();
      expect(cityLine).toBeNull();
      expect(info?.textContent?.trim()).toBe('Only info');
    });
  });

  describe('E2E attributes', () => {
    it('should have data-what attribute on main container', () => {
      // Arrange
      const address: Address = {
        street: 'Test Street',
        streetNumber: '1',
        zipCode: '12345',
        city: 'Test City',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const mainContainer = compiled.querySelector('[data-what="address-display"]');
      expect(mainContainer).toBeTruthy();
    });

    it('should have data-which attributes on all rendered lines', () => {
      // Arrange
      const address: Address = {
        careOf: 'Jane Doe',
        street: 'Test Street',
        streetNumber: '1',
        info: 'Extra info',
        zipCode: '12345',
        city: 'Test City',
      };
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      expect(compiled.querySelector('[data-which="care-of-line"]')).toBeTruthy();
      expect(compiled.querySelector('[data-which="street-line"]')).toBeTruthy();
      expect(compiled.querySelector('[data-which="info"]')).toBeTruthy();
      expect(compiled.querySelector('[data-which="city-line"]')).toBeTruthy();
    });
  });

  describe('Empty or undefined address', () => {
    it('should not render anything when address is undefined', () => {
      // Arrange
      fixture.componentRef.setInput('address', undefined);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const mainContainer = compiled.querySelector('[data-what="address-display"]');
      expect(mainContainer).toBeNull();
    });

    it('should not render anything when address is empty object', () => {
      // Arrange
      const address: Address = {};
      fixture.componentRef.setInput('address', address);

      // Act
      fixture.detectChanges();

      // Assert
      const compiled = fixture.nativeElement as HTMLElement;
      const streetLine = compiled.querySelector('[data-which="street-line"]');
      const cityLine = compiled.querySelector('[data-which="city-line"]');
      const info = compiled.querySelector('[data-which="info"]');

      expect(streetLine).toBeNull();
      expect(cityLine).toBeNull();
      expect(info).toBeNull();
    });
  });
});