import { signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
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 { CartService } from '../../../../core/services/cart/cart.service';
import {
  CatalogFeaturedGroup,
  CatalogFeaturedItem,
} from '../../../../core/services/catalog/catalog.interface';
import { CatalogService } from '../../../../core/services/catalog/catalog.service';
import { Tenant } from '../../../../core/services/tenant.interface';
import { TenantService } from '../../../../core/services/tenant.service';
import { ToastService } from '../../../../core/services/toast.service';
import { ProductListComponent } from '../../../../shared/components/product-list/product-list.component';
import { StoreHomePageComponent } from './store-home-page.component';
import {
  STORE_HOME_PRODUCTS_ERROR_MESSAGE,
  StoreHomeCatalogResolvedData,
} from './store-home-page.resolver';

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

function createCatalog(
  items = pageOneItems,
  currentPage = 1,
  lastPage = 2,
): CatalogFeaturedGroup[] {
  return [
    {
      id: 7,
      title: 'Destacados',
      layout: 'column_with_image',
      group_layout: 'paginated',
      group_order: 0,
      items: createItemsPage(items, currentPage, lastPage),
    },
  ];
}

function provideActivatedRoute(catalogData: StoreHomeCatalogResolvedData) {
  return {
    provide: ActivatedRoute,
    useValue: { snapshot: { data: { catalogData } } },
  };
}

const pageOneItems: CatalogFeaturedItem[] = [
  {
    id: 1,
    nombre: 'Auriculares Bluetooth',
    precio: '24999.00',
    image: '/catalog/auriculares.jpg',
  },
  {
    id: 2,
    nombre: 'Teclado Mecanico',
    precio: '18999.00',
    image: null,
  },
];

function createTenant(mainCarouselImages?: string[]): Tenant {
  return {
    id: 1,
    codigo: 'acme',
    nombre: 'Acme',
    dominio: 'acme.com',
    primary_color: '#6376f3',
    secondary_color: '#a0a0a0',
    danger_color: '#dc3545',
    success_color: '#198754',
    header_bg_color: '#ffffff',
    footer_bg_color: '#ffffff',
    header_logo: '/header.png',
    footer_logo: '/footer.png',
    categories: [],
    main_carousel_images: mainCarouselImages,
  };
}

function createTenantServiceStub(tenant: Tenant | null) {
  const tenantState = signal(tenant);

  return {
    tenant: tenantState.asReadonly(),
    getTenant: () => tenantState(),
  };
}

describe('StoreHomePageComponent', () => {
  beforeEach(() => vi.restoreAllMocks());

  it('renders the CatalogNew groups resolved by the route', async () => {
    const catalogServiceStub = {
      getCatalog: vi.fn(),
      getFeaturedGroupItems: vi.fn(),
    };

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

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();
    const element = fixture.nativeElement as HTMLElement;

    expect(catalogServiceStub.getCatalog).not.toHaveBeenCalled();
    expect(element.querySelector('.store-section__title')?.textContent?.trim()).toBe('Destacados');
    expect(element.querySelectorAll('app-product-column-with-image')).toHaveLength(2);
    expect(element.textContent).toContain('Auriculares Bluetooth');
    expect(element.querySelector('[data-testid="paginator-status"]')?.textContent?.trim()).toBe(
      '1/2',
    );
  });

  it('renders the tenant main carousel images at the beginning of the home page', async () => {
    const mainCarouselImages = [
      'https://example.com/carousel-1.webp',
      'https://example.com/carousel-2.webp',
    ];

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute({ response: createCatalog(), error: null }),
        {
          provide: CatalogService,
          useValue: { getCatalog: vi.fn(), getFeaturedGroupItems: vi.fn() },
        },
        {
          provide: TenantService,
          useValue: createTenantServiceStub(createTenant(mainCarouselImages)),
        },
      ],
    }).compileComponents();

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

    const element = fixture.nativeElement as HTMLElement;
    const carousel = element.querySelector('app-main-carousel');
    const images = carousel?.querySelectorAll('.main-carousel__image');

    expect(element.firstElementChild).toBe(carousel);
    expect(images).toHaveLength(2);
    expect(images?.[0].getAttribute('src')).toBe(mainCarouselImages[0]);
    expect(images?.[1].getAttribute('src')).toBeNull();
    expect(element.querySelectorAll('app-product-column-with-image img')).toHaveLength(0);

    images?.[0].dispatchEvent(new Event('load'));
    fixture.detectChanges();

    expect(images?.[1].getAttribute('src')).toBe(mainCarouselImages[1]);
    expect(element.querySelectorAll('app-product-column-with-image img')).toHaveLength(1);

    fixture.destroy();
  });

  it('does not render the main carousel when the tenant has no images', async () => {
    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute({ response: createCatalog(), error: null }),
        {
          provide: CatalogService,
          useValue: { getCatalog: vi.fn(), getFeaturedGroupItems: vi.fn() },
        },
        {
          provide: TenantService,
          useValue: createTenantServiceStub(createTenant([])),
        },
      ],
    }).compileComponents();

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

    expect((fixture.nativeElement as HTMLElement).querySelector('app-main-carousel')).toBeNull();
  });

  it('requests another page for the selected featured group', async () => {
    const pageTwoItems = [{ id: 3, nombre: 'Mouse Gamer', precio: '15999.00', image: null }];
    const catalogServiceStub = {
      getCatalog: vi.fn(),
      getFeaturedGroupItems: vi.fn().mockReturnValue(of(createItemsPage(pageTwoItems, 2, 2))),
      withCustomLoading: vi.fn(),
    };
    catalogServiceStub.withCustomLoading.mockReturnValue(catalogServiceStub);

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute({ response: createCatalog(), error: null }),
        { 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.withCustomLoading).toHaveBeenCalledOnce();
    expect(catalogServiceStub.getFeaturedGroupItems).toHaveBeenCalledWith(7, { page: 2 });
    expect(element.textContent).toContain('Mouse Gamer');
    expect(element.querySelector('[data-testid="paginator-status"]')?.textContent?.trim()).toBe(
      '2/2',
    );
  });

  it('disables only the group paginator while its page request is in flight', async () => {
    const nextPage = new Subject<ApiPaginatedResponse<CatalogFeaturedItem[]>>();
    const catalogServiceStub = {
      getCatalog: vi.fn(),
      getFeaturedGroupItems: vi.fn().mockReturnValue(nextPage.asObservable()),
      withCustomLoading: vi.fn(),
    };
    catalogServiceStub.withCustomLoading.mockReturnValue(catalogServiceStub);

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

    nextPage.next(createItemsPage(pageOneItems, 2, 2));
    nextPage.complete();
    fixture.detectChanges();

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

  it('shows an empty state when CatalogNew returns no groups', async () => {
    const catalogServiceStub = { getCatalog: vi.fn(), getFeaturedGroupItems: vi.fn() };

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

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

    expect((fixture.nativeElement as HTMLElement).textContent).toContain(
      'No hay productos disponibles en este momento.',
    );
  });

  it('shows an error when the catalog resolver fails', async () => {
    const catalogServiceStub = { getCatalog: vi.fn(), getFeaturedGroupItems: 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();

    expect((fixture.nativeElement as HTMLElement).textContent).toContain(
      'No pudimos cargar los productos en este momento.',
    );
  });

  it('adds a product-list cart event to the cart', async () => {
    const catalogServiceStub = { getCatalog: vi.fn(), getFeaturedGroupItems: vi.fn() };
    const cartServiceStub = {
      addItem: vi.fn().mockReturnValue(
        of({ data: {}, message: 'Producto agregado correctamente' }),
      ),
    };
    const toastServiceStub = { success: vi.fn(), danger: vi.fn() };

    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute({ response: createCatalog(), error: null }),
        { provide: CatalogService, useValue: catalogServiceStub },
        { provide: CartService, useValue: cartServiceStub },
        { provide: ToastService, useValue: toastServiceStub },
      ],
    }).compileComponents();

    const fixture = TestBed.createComponent(StoreHomePageComponent);
    fixture.detectChanges();
    const productList = fixture.debugElement.query(By.directive(ProductListComponent))
      .componentInstance as ProductListComponent;

    productList.addToCart.emit({ product: pageOneItems[0], quantity: 3, variant: 12 });

    expect(cartServiceStub.addItem).toHaveBeenCalledWith(1, 12, 3);
    expect(toastServiceStub.success).toHaveBeenCalledWith('Producto agregado correctamente');
  });
});
