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 { CatalogAvailabilityService } from '../../../../core/services/catalog/catalog-availability.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 { HeroBannerComponent } from '../../../../shared/components/hero-banner/hero-banner.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,
      code: 'destacados',
      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,
    type: 'product',
    nombre: 'Auriculares Bluetooth',
    precio: '24999.00',
    image: '/catalog/auriculares.jpg',
  },
  {
    id: 2,
    type: 'product',
    nombre: 'Teclado Mecanico',
    precio: '18999.00',
    image: null,
  },
];

function createTenant(extras: Tenant['extras'] = {}): 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',
    website_type_code: 'shopit',
    website_type: {
      codigo: 'shopit',
      nombre: 'ShopIt',
      primary_color: null,
      secondary_color: null,
      danger_color: null,
      success_color: null,
      site_logo: null,
    },
    extras,
    categories: [],
  };
}

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('app-store-section')?.id).toBe('destacados');
    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('reloads the catalog when availability changes', async () => {
    const refreshedCatalog = createCatalog();
    const catalogServiceStub = {
      getCatalog: vi.fn().mockReturnValue(of(refreshedCatalog)),
      getFeaturedGroupItems: vi.fn(),
      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();
    TestBed.inject(CatalogAvailabilityService).notifyAvailabilityChanged();

    expect(catalogServiceStub.getCatalog).toHaveBeenCalledOnce();
  });

  it('renders the carousel URLs received in tenant extras', async () => {
    const carousel = ['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({ carousel })),
        },
      ],
    }).compileComponents();

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

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

    expect(element.firstElementChild).toBe(carouselElement);
    expect(images).toHaveLength(2);
    expect(images?.[0].getAttribute('src')).toBe(carousel[0]);
  });

  it('renders the hero extra and the active tenant event', async () => {
    const tenant = createTenant({
      heroConfig: {
        title_html: '<h1>Fiesta Fútbol Infantil</h1>',
        description_html: '<p>Viví una jornada inolvidable de fútbol infantil.</p>',
        background_image_id: 'https://example.com/hero.jpg',
      },
    });
    tenant.event = {
      title: 'Fiesta Fútbol Infantil',
      location: 'Rosario, Santa Fe',
      dates: [
        {
          id: 20,
          date: '2026-12-05',
          time_start: '09:00:00',
          time_end: '18:00:00',
        },
        {
          id: 21,
          date: '2026-12-06',
          time_start: '09:00:00',
          time_end: '18:00:00',
        },
      ],
    };
    tenant.event_date_text = '5 y 6 de Diciembre 2026';
    tenant.website_type_code = 'onticket';
    tenant.social_media = [
      {
        code: 'whatsapp',
        icon: 'fa-brands fa-whatsapp',
        name: 'WhatsApp',
        url: 'https://wa.me/5493410000000',
      },
    ];
    tenant.website_type = {
      codigo: 'onticket',
      nombre: 'OnTicket',
      primary_color: null,
      secondary_color: null,
      danger_color: null,
      success_color: null,
      site_logo: null,
    };

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

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

    const hero = (fixture.nativeElement as HTMLElement).querySelector(
      'app-hero-banner .hero-banner',
    ) as HTMLElement;
    const heroImage = hero.querySelector('img');

    expect(hero).not.toBeNull();
    expect(heroImage?.getAttribute('src')).toBe('https://example.com/hero.jpg');
    expect(hero.textContent).toContain('Fiesta Fútbol Infantil');
    expect(hero.textContent).toContain('Viví una jornada inolvidable de fútbol infantil.');
    expect(hero.textContent).toContain('Rosario, Santa Fe');
    expect(hero.textContent).toContain('5 y 6 de Diciembre 2026');
    const heroComponent = fixture.debugElement.query(By.directive(HeroBannerComponent))
      .componentInstance as HeroBannerComponent;
    expect(heroComponent.eventConfig?.contact).toEqual(tenant.social_media);
  });

  it('renders additional information as an HTML section', async () => {
    const description =
      '<p>Consultá los <strong>términos del evento</strong>.</p><ul><li>Ingreso con DNI</li></ul>';

    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({ additionalInfoConfig: { description } }),
          ),
        },
      ],
    }).compileComponents();

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

    const element = fixture.nativeElement as HTMLElement;
    const section = element.querySelector('.store-home__additional-info');
    const sections = element.querySelectorAll(':scope > app-store-section');

    expect(sections[sections.length - 1]).toBe(section);
    expect(section?.querySelector('.store-section__title')?.textContent?.trim()).toBe(
      'Información adicional',
    );
    expect(section?.querySelector('strong')?.textContent).toBe('términos del evento');
    expect(section?.querySelector('li')?.textContent).toBe('Ingreso con DNI');
  });

  it('does not render the additional information section without content', 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({ additionalInfoConfig: { description: '   ' } }),
          ),
        },
      ],
    }).compileComponents();

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

    expect(
      (fixture.nativeElement as HTMLElement).querySelector('.store-home__additional-info'),
    ).toBeNull();
  });

  it('renders only the active event data when the tenant does not have the hero extra', async () => {
    const tenant = createTenant();
    tenant.event = {
      title: 'Fiesta Fútbol Infantil',
      location: 'Sunchales, Santa Fe',
      dates: [
        {
          id: 25,
          date: '2026-10-09',
          time_start: '09:00:00',
          time_end: '18:00:00',
        },
      ],
    };
    await TestBed.configureTestingModule({
      imports: [StoreHomePageComponent],
      providers: [
        provideActivatedRoute({ response: createCatalog(), error: null }),
        {
          provide: CatalogService,
          useValue: { getCatalog: vi.fn(), getFeaturedGroupItems: vi.fn() },
        },
        {
          provide: TenantService,
          useValue: createTenantServiceStub(tenant),
        },
      ],
    }).compileComponents();

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

    const element = fixture.nativeElement as HTMLElement;
    const heroComponent = fixture.debugElement.query(By.directive(HeroBannerComponent))
      .componentInstance as HeroBannerComponent;

    expect(heroComponent.heroConfig).toBeNull();
    expect(element.querySelector('.hero-banner')).toBeNull();
    expect(element.querySelector('.hero-banner-container')).toBeNull();
    expect(element.querySelector('.hero-content')).toBeNull();
    expect(element.querySelector('.event-card')).not.toBeNull();
    expect(element.textContent).toContain('Fiesta Fútbol Infantil');
    expect(element.textContent).toContain('Sunchales, Santa Fe');
    expect(element.textContent).toContain('2026-10-09');
  });

  it('requests another page for the selected featured group', async () => {
    const pageTwoItems: CatalogFeaturedItem[] = [
      { id: 3, type: 'product', 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');
  });
});
