import { TestBed, getTestBed } from '@angular/core/testing';
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';

import { ApiPaginatedResponse } from '../../../core/services/api-paginated-response.interface';
import {
  CatalogFeaturedItems,
  CatalogGroupLayout,
} from '../../../core/services/catalog/catalog.interface';
import { ProductListComponent, ProductListItem, ProductListLayout } from './product-list.component';

describe('ProductListComponent', () => {
  const items: ProductListItem[] = [
    {
      id: 1,
      nombre: 'Producto uno',
      descripcion: 'Primera descripcion',
      precio: '100.00',
      image: '/images/one.png',
      variants: [],
    },
    {
      id: 2,
      nombre: 'Producto dos',
      descripcion: 'Segunda descripcion',
      precio: 200,
      image: null,
      variants: [],
    },
  ];

  beforeAll(() => {
    try {
      getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
    } catch {
      // Test environment may already be initialized by another setup entrypoint.
    }
  });

  afterEach(() => {
    vi.unstubAllGlobals();
    TestBed.resetTestingModule();
  });

  async function render(
    layout: ProductListLayout,
    productItems: CatalogFeaturedItems = paginatedItems(),
    groupLayout: CatalogGroupLayout = 'paginated',
  ) {
    await TestBed.configureTestingModule({ imports: [ProductListComponent] }).compileComponents();
    const fixture = TestBed.createComponent(ProductListComponent);
    fixture.componentRef.setInput('layout', layout);
    fixture.componentRef.setInput('groupLayout', groupLayout);
    fixture.componentRef.setInput('items', productItems);
    fixture.detectChanges();

    return fixture;
  }

  function paginatedItems(currentPage = 1, lastPage = 2): ApiPaginatedResponse<ProductListItem[]> {
    return {
      data: items,
      meta: {
        current_page: currentPage,
        from: 1,
        last_page: lastPage,
        links: [],
        path: '/catalog/featured-groups/1/items',
        per_page: 12,
        to: 2,
        total: 13,
      },
      links: {
        first: '/catalog/featured-groups/1/items?page=1',
        last: '/catalog/featured-groups/1/items?page=2',
        prev: null,
        next: '/catalog/featured-groups/1/items?page=2',
      },
    };
  }

  it('renders row products in the adaptive grid', async () => {
    const fixture = await render('row');
    const element = fixture.nativeElement as HTMLElement;

    expect(element.querySelector('.product-list--row')).not.toBeNull();
    expect(element.querySelector('.product-list--adaptive')).not.toBeNull();
    expect(element.querySelectorAll('app-product-row-card')).toHaveLength(2);
    expect(element.querySelector('app-product-column-with-image')).toBeNull();
  });

  it('replaces the row layout with column with cart on mobile', async () => {
    const matchMedia = vi.fn().mockReturnValue({
      matches: true,
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
    } as unknown as MediaQueryList);
    vi.stubGlobal('matchMedia', matchMedia);

    const fixture = await render('row');
    await fixture.whenStable();
    fixture.detectChanges();
    const element = fixture.nativeElement as HTMLElement;

    expect(matchMedia).toHaveBeenCalledWith('(max-width: 767.98px)');
    expect(element.querySelector('.product-list--column')).not.toBeNull();
    expect(element.querySelectorAll('app-product-vertical-with-cart-card')).toHaveLength(2);
    expect(element.querySelector('app-product-row-card')).toBeNull();
  });

  it('renders image products next to each other in the column grid', async () => {
    const fixture = await render('column_with_image');
    const element = fixture.nativeElement as HTMLElement;

    expect(element.querySelector('.product-list--column')).not.toBeNull();
    expect(element.querySelectorAll('app-product-column-with-image')).toHaveLength(2);
  });

  it('renders cart products next to each other in the column grid', async () => {
    const fixture = await render('column_with_cart');

    expect(
      (fixture.nativeElement as HTMLElement).querySelectorAll(
        'app-product-vertical-with-cart-card',
      ),
    ).toHaveLength(2);
  });

  it('emits a direct-purchase event with the row quantity and selected variant', async () => {
    const fixture = await render('row');
    const buySpy = vi.fn();
    fixture.componentInstance.buy.subscribe(buySpy);
    const rowCard = fixture.nativeElement.querySelector('app-product-row-card') as HTMLElement;

    (rowCard.querySelector('.btn-primary') as HTMLButtonElement).click();

    expect(buySpy).toHaveBeenCalledWith({
      product: items[0],
      quantity: 1,
      variant: null,
      directPurchase: true,
    });
  });

  it('emits a direct-purchase event with the column quantity', async () => {
    const fixture = await render('column_with_cart');
    const buySpy = vi.fn();
    fixture.componentInstance.buy.subscribe(buySpy);
    const card = fixture.nativeElement.querySelector(
      'app-product-vertical-with-cart-card',
    ) as HTMLElement;

    (card.querySelector('.btn-primary') as HTMLButtonElement).click();

    expect(buySpy).toHaveBeenCalledWith({
      product: items[0],
      quantity: 1,
      variant: null,
      directPurchase: true,
    });
  });

  it('passes column variants through to the purchase event', async () => {
    const itemWithVariants: ProductListItem = {
      ...items[0],
      variants: [
        { id: 91, stock_tecnico: 3, values: { fecha: '10 de octubre' } },
        { id: 92, stock_tecnico: 4, values: { fecha: '11 de octubre' } },
      ],
    };
    const fixture = await render('column_with_cart', [itemWithVariants]);
    const buySpy = vi.fn();
    fixture.componentInstance.buy.subscribe(buySpy);

    const card = fixture.nativeElement.querySelector(
      'app-product-vertical-with-cart-card',
    ) as HTMLElement;
    (card.querySelector('.btn-primary') as HTMLButtonElement).click();

    expect(buySpy).toHaveBeenCalledWith({
      product: itemWithVariants,
      quantity: 1,
      variant: 91,
      directPurchase: true,
    });
  });

  it('renders pagination and emits the requested page', async () => {
    const fixture = await render('row');
    const pageChangeSpy = vi.fn();
    fixture.componentInstance.pageChange.subscribe(pageChangeSpy);

    const nextButton = (fixture.nativeElement as HTMLElement).querySelector(
      '[data-testid="paginator-next"]',
    ) as HTMLButtonElement;
    nextButton.click();

    expect(pageChangeSpy).toHaveBeenCalledWith(2);
  });

  it('renders plain items without pagination', async () => {
    const fixture = await render('row', items, 'simple');
    const element = fixture.nativeElement as HTMLElement;

    expect(element.querySelectorAll('app-product-row-card')).toHaveLength(2);
    expect(element.querySelector('.product-list--adaptive')).not.toBeNull();
    expect(element.querySelector('app-paginator')).toBeNull();
  });

  it('keeps simple vertical groups to one full-width item per row', async () => {
    const fixture = await render('column_with_image', items, 'simple_vertical');
    const element = fixture.nativeElement as HTMLElement;

    expect(element.querySelector('.product-list--simple-vertical')).not.toBeNull();
    expect(element.querySelector('.product-list--adaptive')).toBeNull();
    expect(element.querySelectorAll('app-product-column-with-image')).toHaveLength(2);
  });

  it('renders carousel groups with the reusable carousel', async () => {
    const fixture = await render('column_with_image', items, 'carousel');
    const element = fixture.nativeElement as HTMLElement;

    expect(element.querySelector('app-carousel')).not.toBeNull();
    expect(element.querySelector('.product-list')).toBeNull();
    expect(element.querySelectorAll('app-product-column-with-image')).toHaveLength(2);
    expect(element.querySelectorAll('.product-list__item--carousel')).toHaveLength(2);
    expect(element.querySelector('app-paginator')).toBeNull();
  });
});
