import { HttpErrorResponse } from '@angular/common/http';
import {
  ChangeDetectionStrategy,
  Component,
  DestroyRef,
  Injector,
  inject,
  signal,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute, Router } from '@angular/router';
import {
  catchError,
  combineLatest,
  distinctUntilChanged,
  finalize,
  map,
  of,
  startWith,
  switchMap,
  tap,
} from 'rxjs';

import { AuthService } from '../../../../core/services/auth/auth.service';
import { CartService } from '../../../../core/services/cart/cart.service';
import {
  CheckoutService,
  isExpiredStockReservationResponse,
} from '../../../../core/services/checkout.service';
import {
  CatalogGroupLayout,
  CategoryItemsResponse,
} from '../../../../core/services/catalog/catalog.interface';
import { CatalogService } from '../../../../core/services/catalog/catalog.service';
import { CatalogAvailabilityService } from '../../../../core/services/catalog/catalog-availability.service';
import { TenantService } from '../../../../core/services/tenant.service';
import { ToastService } from '../../../../core/services/toast.service';
import {
  ProductListBuyEvent,
  ProductListCartEvent,
  ProductListComponent,
} from '../../../../shared/components/product-list/product-list.component';

interface CategoryRouteState {
  categoryId: number;
  page: number;
}

@Component({
  selector: 'app-category-items-page',
  imports: [ProductListComponent],
  templateUrl: './category-items-page.component.html',
  styleUrl: './category-items-page.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CategoryItemsPageComponent {
  private readonly cartService = inject(CartService);
  private readonly catalogAvailabilityService = inject(CatalogAvailabilityService);
  private readonly catalogService = inject(CatalogService);
  private readonly injector = inject(Injector);
  private readonly destroyRef = inject(DestroyRef);
  private readonly route = inject(ActivatedRoute);
  private readonly router = inject(Router);
  private readonly tenantService = inject(TenantService);
  private readonly toastService = inject(ToastService);

  protected readonly results = signal<CategoryItemsResponse | null>(null);
  protected readonly error = signal<string | null>(null);
  protected readonly creatingDirectPurchase = signal(false);
  protected readonly savingProductIds = signal<ReadonlySet<number>>(new Set<number>());
  protected readonly paginatedLayout: CatalogGroupLayout = 'paginated';

  constructor() {
    const routeState$ = combineLatest([this.route.paramMap, this.route.queryParamMap]).pipe(
      map(
        ([params, queryParams]): CategoryRouteState => ({
          categoryId: this.parsePositiveInteger(params.get('id')),
          page: this.parsePositiveInteger(queryParams.get('page'), 1),
        }),
      ),
      distinctUntilChanged(
        (previous, current) =>
          previous.categoryId === current.categoryId && previous.page === current.page,
      ),
      tap(() => {
        this.results.set(null);
        this.error.set(null);
      }),
    );

    combineLatest([
      routeState$,
      this.catalogAvailabilityService.availabilityChanged$.pipe(startWith(undefined)),
    ])
      .pipe(
        map(([routeState]) => routeState),
        switchMap(({ categoryId, page }) => {
          if (categoryId === 0) {
            this.error.set('La categoría solicitada no es válida.');

            return of(null);
          }

          return this.catalogService.getCategoryItems(categoryId, { page }).pipe(
            catchError(() => {
              this.error.set('No pudimos cargar los productos de esta categoría.');

              return of(null);
            }),
          );
        }),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe((results) => {
        this.results.set(results);
      });
  }

  protected onPageChange(page: number): void {
    void this.router.navigate([], {
      relativeTo: this.route,
      queryParams: { page },
      queryParamsHandling: 'merge',
    });
  }

  protected async onBuyProduct(event: ProductListBuyEvent): Promise<void> {
    if (!event.directPurchase) {
      await this.router.navigate(['/producto', event.product.id]);
      return;
    }

    if (this.creatingDirectPurchase()) {
      return;
    }

    if (!this.injector.get(AuthService).user()) {
      await this.router.navigate(['/login'], {
        queryParams: {
          returnUrl: event.reservedInCart ? this.router.url : `/producto/${event.product.id}`,
        },
      });
      return;
    }

    const tenant = this.tenantService.tenant();
    if (!tenant) {
      this.toastService.danger('No se pudo identificar la tienda.');
      return;
    }

    this.creatingDirectPurchase.set(true);
    try {
      const reservedCartId = event.reservedInCart ? (this.cartService.cart()?.id ?? null) : null;
      if (event.reservedInCart && reservedCartId === null) {
        this.toastService.danger('No se pudo identificar el carrito reservado.');
        return;
      }
      const purchase = await this.injector.get(CheckoutService).startCheckout(
        tenant.codigo,
        reservedCartId !== null
          ? { cart_id: reservedCartId }
          : {
              direct_items: [
                {
                  catalog_item_id: event.product.id,
                  variant_id: event.variant ?? null,
                  cantidad: event.quantity,
                },
              ],
            },
      );
      await this.router.navigate(['/checkout', purchase.id]);
    } catch (error) {
      console.error('Failed to create direct purchase:', error);
      const message =
        error instanceof HttpErrorResponse && typeof error.error?.message === 'string'
          ? error.error.message
          : 'No se pudo iniciar la compra directa.';
      this.toastService.danger(message);

      if (error instanceof HttpErrorResponse && isExpiredStockReservationResponse(error.error)) {
        this.cartService.loadCart(true).subscribe({
          error: (refreshError) => console.error('Error refreshing expired cart', refreshError),
        });
      }
    } finally {
      this.creatingDirectPurchase.set(false);
    }
  }

  protected onAddToCart(event: ProductListCartEvent): void {
    const productId = event.product.id;
    if (this.savingProductIds().has(productId)) {
      return;
    }

    this.setProductSaving(productId, true);
    this.cartService
      .addItem(productId, event.variant ?? null, event.quantity)
      .pipe(finalize(() => this.setProductSaving(productId, false)))
      .subscribe({
        next: (response) => {
          this.toastService.success(response.message || 'Producto agregado al carrito');
        },
        error: (error: HttpErrorResponse) => {
          const message = error.error?.message || 'No se pudo agregar el producto al carrito.';
          this.toastService.danger(message);
        },
      });
  }

  private setProductSaving(productId: number, saving: boolean): void {
    this.savingProductIds.update((current) => {
      const updated = new Set(current);
      saving ? updated.add(productId) : updated.delete(productId);
      return updated;
    });
  }

  private parsePositiveInteger(value: string | null, fallback = 0): number {
    const parsed = Number(value ?? fallback);

    return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
  }
}
