import { ComponentFixture, TestBed } from '@angular/core/testing';
import {
  ProductInfoComponent,
  ProductInfoItem,
} from './product-info.component';
import { MockComponents, MockDirectives } from 'ng-mocks';
import { ProductFormatComponent } from '@isa/shared/product-format';
import { ProductImageDirective } from '@isa/shared/product-image';
import { ProductRouterLinkDirective } from '@isa/shared/product-router-link';
import { LabelComponent } from '@isa/ui/label';
import { By } from '@angular/platform-browser';

describe('ProductInfoComponent', () => {
  let component: ProductInfoComponent;
  let fixture: ComponentFixture<ProductInfoComponent>;

  const mockProductItem: ProductInfoItem = {
    product: {
      id: 1,
      name: 'Test Product',
      ean: '1234567890123',
      contributors: 'Test Contributors',
      format: 'Test Format',
      formatDetail: 'Test Format Detail',
    },
    retailPrice: {
      value: {
        value: 19.99,
        currencySymbol: '€',
      },
    },
    tag: 'Prio 1',
  } as ProductInfoItem;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [ProductInfoComponent],
    })
      .overrideComponent(ProductInfoComponent, {
        remove: {
          imports: [
            ProductFormatComponent,
            ProductImageDirective,
            ProductRouterLinkDirective,
            LabelComponent,
          ],
        },
        add: {
          imports: [
            MockComponents(ProductFormatComponent, LabelComponent),
            MockDirectives(ProductImageDirective, ProductRouterLinkDirective),
          ],
        },
      })
      .compileComponents();

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

  describe('Component Setup and Initialization', () => {
    it('should create', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      expect(component).toBeTruthy();
    });

    it('should have default orientation as horizontal', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.detectChanges();
      expect(component.orientation()).toBe('horizontal');
    });

    it('should accept vertical orientation', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.componentRef.setInput('orientation', 'vertical');
      fixture.detectChanges();
      expect(component.orientation()).toBe('vertical');
    });

    it('should have correct CSS classes', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.detectChanges();
      expect(component.classList).toEqual([
        'grid',
        'grid-cols-[3.5rem,1fr]',
        'gap-6',
        'text-isa-neutral-900',
      ]);
    });

    it('should set host attributes correctly', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.detectChanges();

      const hostElement = fixture.debugElement.nativeElement;
      expect(hostElement.getAttribute('data-what')).toBe('product-info');
      expect(hostElement.getAttribute('data-which')).toBe('remission-product');
      expect(hostElement.getAttribute('data-ean')).toBe('1234567890123');
    });
  });

  describe('Template Rendering', () => {
    beforeEach(() => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.detectChanges();
    });

    it('should display product image with correct attributes', () => {
      const productImage = fixture.debugElement.query(By.css('img'));
      expect(productImage).toBeTruthy();
      expect(productImage.nativeElement.getAttribute('data-what')).toBe(
        'product-image',
      );
      expect(productImage.nativeElement.alt).toBe('Test Product');
    });

    it('should display product contributors', () => {
      const contributorsElement = fixture.debugElement.query(
        By.css('[data-what="product-contributors"]'),
      );
      expect(contributorsElement).toBeTruthy();
      expect(contributorsElement.nativeElement.textContent.trim()).toBe(
        'Test Contributors',
      );
    });

    it('should display product name', () => {
      const nameElement = fixture.debugElement.query(
        By.css('[data-what="product-name"]'),
      );
      expect(nameElement).toBeTruthy();
      expect(nameElement.nativeElement.textContent.trim()).toBe('Test Product');
    });

    it('should display formatted price', () => {
      const priceElement = fixture.debugElement.query(
        By.css('[data-what="product-price"]'),
      );
      expect(priceElement).toBeTruthy();
      expect(priceElement.nativeElement.textContent.trim()).toBe('€19.99');
    });

    it('should display product EAN', () => {
      const eanElement = fixture.debugElement.query(
        By.css('[data-what="product-ean"]'),
      );
      expect(eanElement).toBeTruthy();
      expect(eanElement.nativeElement.textContent.trim()).toBe('1234567890123');
    });

    it('should render product format component with correct inputs', () => {
      const formatComponent = fixture.debugElement.query(
        By.css('shared-product-format'),
      );
      expect(formatComponent).toBeTruthy();
      expect(formatComponent.nativeElement.getAttribute('data-what')).toBe(
        'product-format',
      );
    });

    it('should display remission label when tag is present', () => {
      const labelElement = fixture.debugElement.query(
        By.css('[data-what="remission-label"]'),
      );
      expect(labelElement).toBeTruthy();
      expect(labelElement.nativeElement.textContent.trim()).toBe('Prio 1');
    });

    it('should not display remission label when tag is not present', () => {
      const itemWithoutTag: ProductInfoItem = {
        ...mockProductItem,
        tag: undefined,
      };

      fixture.componentRef.setInput('item', itemWithoutTag);
      fixture.detectChanges();

      const labelElement = fixture.debugElement.query(
        By.css('[data-what="remission-label"]'),
      );
      expect(labelElement).toBeFalsy();
    });
  });

  describe('Orientation Behavior', () => {
    it('should apply horizontal layout classes when orientation is horizontal', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.componentRef.setInput('orientation', 'horizontal');
      fixture.detectChanges();

      const layoutDiv = fixture.debugElement.query(
        By.css('.grid.grid-flow-row.gap-2'),
      );
      expect(layoutDiv).toBeTruthy();
      expect(layoutDiv.nativeElement.classList.contains('gap-2')).toBe(true);
    });

    it('should apply vertical layout classes when orientation is vertical', () => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.componentRef.setInput('orientation', 'vertical');
      fixture.detectChanges();

      const layoutDiv = fixture.debugElement.query(By.css('.grid-flow-row'));
      expect(layoutDiv).toBeTruthy();
      expect(layoutDiv.nativeElement.classList.contains('gap-2')).toBe(true);
    });
  });

  describe('Input Validation', () => {
    it('should handle item input changes', () => {
      const newItem: ProductInfoItem = {
        ...mockProductItem,
        product: {
          ...mockProductItem.product,
          name: 'Updated Product Name',
          ean: '9876543210987',
        },
      };

      fixture.componentRef.setInput('item', mockProductItem);
      fixture.detectChanges();

      fixture.componentRef.setInput('item', newItem);
      fixture.detectChanges();

      const nameElement = fixture.debugElement.query(
        By.css('[data-what="product-name"]'),
      );
      expect(nameElement.nativeElement.textContent.trim()).toBe(
        'Updated Product Name',
      );

      const hostElement = fixture.debugElement.nativeElement;
      expect(hostElement.getAttribute('data-ean')).toBe('9876543210987');
    });

    it('should handle different currency symbols', () => {
      const itemWithDifferentCurrency: ProductInfoItem = {
        ...mockProductItem,
        retailPrice: {
          value: {
            value: 25.5,
            currencySymbol: '$',
          },
        },
      };

      fixture.componentRef.setInput('item', itemWithDifferentCurrency);
      fixture.detectChanges();

      const priceElement = fixture.debugElement.query(
        By.css('[data-what="product-price"]'),
      );
      expect(priceElement.nativeElement.textContent.trim()).toBe('$25.50');
    });

    it('should handle zero price', () => {
      const itemWithZeroPrice: ProductInfoItem = {
        ...mockProductItem,
        retailPrice: {
          value: {
            value: 0,
            currencySymbol: '€',
          },
        },
      };

      fixture.componentRef.setInput('item', itemWithZeroPrice);
      fixture.detectChanges();

      const priceElement = fixture.debugElement.query(
        By.css('[data-what="product-price"]'),
      );
      expect(priceElement.nativeElement.textContent.trim()).toBe('€0.00');
    });
  });

  describe('RemissionItemTags Behavior', () => {
    it('should display Prio1 tag with primary appearance', () => {
      const itemWithPrio1: ProductInfoItem = {
        ...mockProductItem,
        tag: 'Prio 1',
      };

      fixture.componentRef.setInput('item', itemWithPrio1);
      fixture.detectChanges();

      const labelElement = fixture.debugElement.query(
        By.css('[data-what="remission-label"]'),
      );
      expect(labelElement).toBeTruthy();
      expect(labelElement.nativeElement.textContent.trim()).toBe('Prio 1');
    });

    it('should display Prio2 tag with secondary appearance', () => {
      const itemWithPrio2: ProductInfoItem = {
        ...mockProductItem,
        tag: 'Prio 2',
      };

      fixture.componentRef.setInput('item', itemWithPrio2);
      fixture.detectChanges();

      const labelElement = fixture.debugElement.query(
        By.css('[data-what="remission-label"]'),
      );
      expect(labelElement).toBeTruthy();
      expect(labelElement.nativeElement.textContent.trim()).toBe('Prio 2');
    });

    it('should display Pflicht tag with primary appearance', () => {
      const itemWithPflicht: ProductInfoItem = {
        ...mockProductItem,
        tag: 'Pflicht',
      };

      fixture.componentRef.setInput('item', itemWithPflicht);
      fixture.detectChanges();

      const labelElement = fixture.debugElement.query(
        By.css('[data-what="remission-label"]'),
      );
      expect(labelElement).toBeTruthy();
      expect(labelElement.nativeElement.textContent.trim()).toBe('Pflicht');
    });
  });

  describe('Component Structure', () => {
    beforeEach(() => {
      fixture.componentRef.setInput('item', mockProductItem);
      fixture.detectChanges();
    });

    it('should have proper grid structure', () => {
      const hostElement = fixture.debugElement.nativeElement;
      expect(hostElement.classList.contains('grid')).toBe(true);
      expect(hostElement.classList.contains('grid-cols-[3.5rem,1fr]')).toBe(
        true,
      );
      expect(hostElement.classList.contains('gap-6')).toBe(true);
    });

    it('should maintain data attributes for testing', () => {
      const dataWhatElements = fixture.debugElement.queryAll(
        By.css('[data-what]'),
      );
      expect(dataWhatElements.length).toBeGreaterThan(0);

      const expectedDataWhatValues = [
        'product-image',
        'remission-label',
        'product-contributors',
        'product-name',
        'product-price',
        'product-format',
        'product-ean',
      ];

      expectedDataWhatValues.forEach((value) => {
        const element = fixture.debugElement.query(
          By.css(`[data-what="${value}"]`),
        );
        expect(element).toBeTruthy();
      });
    });
  });

  describe('Edge Cases', () => {
    it('should handle empty product name', () => {
      const itemWithEmptyName: ProductInfoItem = {
        ...mockProductItem,
        product: {
          ...mockProductItem.product,
          name: '',
        },
      };

      fixture.componentRef.setInput('item', itemWithEmptyName);
      fixture.detectChanges();

      const nameElement = fixture.debugElement.query(
        By.css('[data-what="product-name"]'),
      );
      expect(nameElement.nativeElement.textContent.trim()).toBe('');
    });

    it('should handle empty contributors', () => {
      const itemWithEmptyContributors: ProductInfoItem = {
        ...mockProductItem,
        product: {
          ...mockProductItem.product,
          contributors: '',
        },
      };

      fixture.componentRef.setInput('item', itemWithEmptyContributors);
      fixture.detectChanges();

      const contributorsElement = fixture.debugElement.query(
        By.css('[data-what="product-contributors"]'),
      );
      expect(contributorsElement.nativeElement.textContent.trim()).toBe('');
    });

    it('should handle long product names gracefully', () => {
      const itemWithLongName: ProductInfoItem = {
        ...mockProductItem,
        product: {
          ...mockProductItem.product,
          name: 'This is a very long product name that might cause layout issues if not handled properly',
        },
      };

      fixture.componentRef.setInput('item', itemWithLongName);
      fixture.detectChanges();

      const nameElement = fixture.debugElement.query(
        By.css('[data-what="product-name"]'),
      );
      expect(nameElement.nativeElement.textContent.trim()).toContain(
        'This is a very long product name',
      );
    });
  });
});
