import { TestBed } from '@angular/core/testing';
import { Subject, of, throwError } from 'rxjs';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { ApiPaginatedResponse } from '../../../../core/services/api-paginated-response.interface';
import { Product } from '../../../../core/services/catalog/catalog.interface';
import { CatalogService } from '../../../../core/services/catalog/catalog.service';
import { StoreHomePageComponent } from './store-home-page.component';

function createPaginatedResponse(
  products: Product[],
  currentPage: number,
  lastPage: number
): ApiPaginatedResponse<Product[]> {
  return {
    data: products,
    meta: {
      current_page: currentPage,
      from: products.length ? 1 : null,
      last_page: lastPage,
      links: [],
      path: '/productos',
      per_page: 12,
      to: products.length || null,
      total: products.length
    },
    links: {
      first: '/productos?page=1',
      last: `/productos?page=${lastPage}`,
      prev: currentPage > 1 ? `/productos?page=${currentPage - 1}` : null,
      next: currentPage < lastPage ? `/productos?page=${currentPage + 1}` : null
    }
  };
}

describe('StoreHomePageComponent', () => {
  const pageOneProducts: Product[] = [
    {
      id: 1,
      tenant_codigo: 'test',
      categoria_id: 10,
      slug: 'auriculares-bluetooth',
      nombre: 'Auriculares Bluetooth',
      precio: '24999'
    },
    {
      id: 2,
      tenant_codigo: 'test',
      categoria_id: 11,
      slug: 'teclado-mecanico',
      nombre: 'Teclado Mecanico',
      precio: '18999'
    }
  ];

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

  it('loads page 1 on init, renders the products title and the fetched product cards', async () => {
    const catalogServiceStub = {
      getProductos: vi.fn().mockReturnValue(of(createPaginatedResponse(pageOneProducts, 1, 3)))
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        {
          provide: CatalogService,
          useValue: catalogServiceStub
        }
      ]
    }).compileComponents();

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();

    const element = fixture.nativeElement as HTMLElement;

    expect(catalogServiceStub.getProductos).toHaveBeenCalledWith({ page: 1 });
    expect(element.querySelector('.store-section__title')?.textContent?.trim()).toBe('Productos');
    expect(element.querySelectorAll('app-product-card')).toHaveLength(2);
    expect(element.textContent).toContain('Auriculares Bluetooth');
    expect(element.textContent).toContain('Teclado Mecanico');
    expect(element.querySelector('[data-testid="paginator-status"]')?.textContent?.trim()).toBe('1/3');
  });

  it('requests the next page when the paginator emits a page change', async () => {
    const catalogServiceStub = {
      getProductos: vi
        .fn()
        .mockReturnValueOnce(of(createPaginatedResponse(pageOneProducts, 1, 3)))
        .mockReturnValueOnce(
          of(
            createPaginatedResponse(
              [
                {
                  id: 3,
                  tenant_codigo: 'test',
                  categoria_id: 12,
                  slug: 'mouse-gamer',
                  nombre: 'Mouse Gamer',
                  precio: '15999'
                }
              ],
              2,
              3
            )
          )
        )
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        {
          provide: CatalogService,
          useValue: catalogServiceStub
        }
      ]
    }).compileComponents();

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();

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

    expect(catalogServiceStub.getProductos).toHaveBeenNthCalledWith(1, { page: 1 });
    expect(catalogServiceStub.getProductos).toHaveBeenNthCalledWith(2, { page: 2 });
    expect(element.querySelector('[data-testid="paginator-status"]')?.textContent?.trim()).toBe('2/3');
    expect(element.textContent).toContain('Mouse Gamer');
  });

  it('disables the paginator while a new page request is in flight', async () => {
    const nextPageSubject = new Subject<ApiPaginatedResponse<Product[]>>();
    const catalogServiceStub = {
      getProductos: vi
        .fn()
        .mockReturnValueOnce(of(createPaginatedResponse(pageOneProducts, 1, 3)))
        .mockReturnValueOnce(nextPageSubject.asObservable())
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        {
          provide: CatalogService,
          useValue: catalogServiceStub
        }
      ]
    }).compileComponents();

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();

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

    expect(
      Array.from(element.querySelectorAll('app-paginator button')).every(
        (button) => (button as HTMLButtonElement).disabled
      )
    ).toBe(true);
    expect(element.textContent).toContain('Actualizando productos...');

    nextPageSubject.next(createPaginatedResponse(pageOneProducts, 2, 3));
    nextPageSubject.complete();
    fixture.detectChanges();

    expect(
      Array.from(element.querySelectorAll('app-paginator button')).some(
        (button) => !(button as HTMLButtonElement).disabled
      )
    ).toBe(true);
  });

  it('shows an empty-state message and hides the paginator when there are no products', async () => {
    const catalogServiceStub = {
      getProductos: vi.fn().mockReturnValue(of(createPaginatedResponse([], 1, 1)))
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        {
          provide: CatalogService,
          useValue: catalogServiceStub
        }
      ]
    }).compileComponents();

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();

    const element = fixture.nativeElement as HTMLElement;

    expect(element.textContent).toContain('No hay productos disponibles en este momento.');
    expect(element.querySelector('app-paginator')).toBeNull();
  });

  it('shows an error message when the catalog request fails', async () => {
    const catalogServiceStub = {
      getProductos: vi.fn().mockReturnValue(throwError(() => new Error('boom')))
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        {
          provide: CatalogService,
          useValue: catalogServiceStub
        }
      ]
    }).compileComponents();

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();

    const element = fixture.nativeElement as HTMLElement;

    expect(element.textContent).toContain('No pudimos cargar los productos en este momento.');
    expect(element.querySelectorAll('app-product-card')).toHaveLength(0);
  });
});
