import { TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { Subject, of } 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';
import {
  STORE_HOME_PRODUCTS_ERROR_MESSAGE,
  StoreHomeProductsResolvedData,
} from './store-home-page.resolver';

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,
    },
  };
}

function createResolvedData(
  response: ApiPaginatedResponse<Product[]>,
): StoreHomeProductsResolvedData {
  return {
    response,
    error: null,
  };
}

function provideActivatedRoute(productsData: StoreHomeProductsResolvedData) {
  return {
    provide: ActivatedRoute,
    useValue: {
      snapshot: {
        data: { productsData },
      },
    },
  };
}

describe('StoreHomePageComponent', () => {
  const pageOneProducts: Product[] = [
    {
      id: 1,
      category_id: 10,
      brand_id: null,
      slug: 'auriculares-bluetooth',
      nombre: 'Auriculares Bluetooth',
      descripcion: 'Auriculares bluetooth de prueba',
      precio: '24999',
      category: 'Tecnologia',
      brand: null,
      images: ['/catalog/auriculares.jpg'],
    },
    {
      id: 2,
      category_id: 11,
      brand_id: null,
      slug: 'teclado-mecanico',
      nombre: 'Teclado Mecanico',
      descripcion: 'Teclado mecanico de prueba',
      precio: '18999',
      category: 'Tecnologia',
      brand: null,
      images: [],
    },
  ];

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

  it('renders the products resolved by the route before component init', async () => {
    const catalogServiceStub = {
      getProductos: vi.fn(),
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute(createResolvedData(createPaginatedResponse(pageOneProducts, 1, 3))),
        {
          provide: CatalogService,
          useValue: catalogServiceStub,
        },
      ],
    }).compileComponents();

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

    const element = fixture.nativeElement as HTMLElement;

    expect(catalogServiceStub.getProductos).not.toHaveBeenCalled();
    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',
    );
    expect(element.querySelector('img')?.getAttribute('fetchpriority')).toBe('high');
  });

  it('requests the next page when the paginator emits a page change', async () => {
    const catalogServiceStub = {
      getProductos: vi.fn().mockReturnValueOnce(
        of(
          createPaginatedResponse(
            [
              {
                id: 3,
                category_id: 12,
                brand_id: null,
                slug: 'mouse-gamer',
                nombre: 'Mouse Gamer',
                descripcion: 'Mouse gamer de prueba',
                precio: '15999',
                category: 'Tecnologia',
                brand: null,
                images: [],
              },
            ],
            2,
            3,
          ),
        ),
      ),
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute(createResolvedData(createPaginatedResponse(pageOneProducts, 1, 3))),
        {
          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).toHaveBeenCalledTimes(1);
    expect(catalogServiceStub.getProductos).toHaveBeenCalledWith({ 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(nextPageSubject.asObservable()),
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute(createResolvedData(createPaginatedResponse(pageOneProducts, 1, 3))),
        {
          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(),
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute(createResolvedData(createPaginatedResponse([], 1, 1))),
        {
          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();
    expect(catalogServiceStub.getProductos).not.toHaveBeenCalled();
  });

  it('shows an error message when the route resolver cannot load the catalog', async () => {
    const catalogServiceStub = {
      getProductos: vi.fn(),
    };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute({
          response: null,
          error: STORE_HOME_PRODUCTS_ERROR_MESSAGE,
        }),
        {
          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);
    expect(catalogServiceStub.getProductos).not.toHaveBeenCalled();
  });
});
