import {
  ChangeDetectionStrategy,
  Component,
  Injector,
  OnDestroy,
  OnInit,
  computed,
  inject,
  signal,
} from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { ActivatedRoute, Router } from '@angular/router';
import { finalize, Subscription } from 'rxjs';

import { CartService } from '../../../../core/services/cart/cart.service';
import { AuthService } from '../../../../core/services/auth/auth.service';
import { CatalogFeaturedGroup } from '../../../../core/services/catalog/catalog.interface';
import { CatalogAvailabilityService } from '../../../../core/services/catalog/catalog-availability.service';
import { CatalogService } from '../../../../core/services/catalog/catalog.service';
import { TenantService } from '../../../../core/services/tenant.service';
import { ToastService } from '../../../../core/services/toast.service';
import {
  CheckoutService,
  isExpiredStockReservationResponse,
  isInsufficientStockResponse,
} from '../../../../core/services/checkout.service';
import {
  ProductListComponent,
  ProductListCartEvent,
  ProductListBuyEvent,
} from '../../../../shared/components/product-list/product-list.component';
import { HeroBannerComponent } from '../../../../shared/components/hero-banner/hero-banner.component';
import { MainCarouselComponent } from '../../../../shared/components/main-carousel/main-carousel.component';
import { StoreSectionComponent } from '../../../../shared/components/store-section/store-section.component';
import {
  STORE_HOME_PRODUCTS_ERROR_MESSAGE,
  StoreHomeCatalogResolvedData,
} from './store-home-page.resolver';

@Component({
  selector: 'app-store-home-page',
  imports: [
    StoreSectionComponent,
    ProductListComponent,
    HeroBannerComponent,
    MainCarouselComponent,
  ],
  templateUrl: './store-home-page.component.html',
  styleUrl: './store-home-page.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StoreHomePageComponent implements OnInit, OnDestroy {
  private readonly cartService = inject(CartService);
  private readonly catalogAvailabilityService = inject(CatalogAvailabilityService);
  private readonly catalogService = inject(CatalogService);
  private readonly injector = inject(Injector);
  private readonly route = inject(ActivatedRoute);
  private readonly router = inject(Router);
  private readonly tenantService = inject(TenantService);
  private readonly toastService = inject(ToastService);

  protected readonly tenant = this.tenantService.tenant;
  protected readonly catalog = signal<CatalogFeaturedGroup[]>([]);
  protected readonly loading = signal(false);
  protected readonly loadingGroupIds = signal<ReadonlySet<number>>(new Set());
  protected readonly error = signal<string | null>(null);
  protected readonly mainCarouselReady = signal(false);
  protected readonly creatingDirectPurchase = signal(false);
  protected readonly unavailableVariantIds = signal<ReadonlySet<number>>(new Set<number>());
  protected readonly savingProductIds = signal<ReadonlySet<number>>(new Set<number>());
  protected readonly mainCarouselImages = computed(() => this.tenant()?.extras?.carousel ?? []);
  protected readonly heroConfig = computed(() => this.tenant()?.extras?.heroConfig ?? null);
  protected readonly additionalInfo = computed(
    () => this.tenant()?.extras?.additionalInfoConfig?.description?.trim() || null,
  );
  protected readonly eventConfig = computed(() => {
    const tenant = this.tenant();
    const contact = tenant?.social_media ?? [];
    const event = tenant?.event;

    if (event) {
      return {
        id: tenant.id,
        title: event.title,
        location: event.location,
        dates_text: tenant.event_date_text,
        dates: event.dates.map((eventDate) => ({
          id: eventDate.id,
          date: eventDate.date,
          start_time: eventDate.time_start,
          end_time: eventDate.time_end,
        })),
        contact,
      };
    }

    return null;
  });
  protected readonly hasMainCarouselImages = computed(() => this.mainCarouselImages().length > 0);

  private catalogRequestSubscription: Subscription | null = null;
  private availabilityChangedSubscription: Subscription | null = null;
  private readonly groupRequestSubscriptions = new Map<number, Subscription>();

  ngOnInit(): void {
    this.availabilityChangedSubscription =
      this.catalogAvailabilityService.availabilityChanged$.subscribe(() => this.loadCatalog(true));

    const resolvedData = this.route.snapshot.data['catalogData'] as
      | StoreHomeCatalogResolvedData
      | undefined;

    if (resolvedData) {
      this.applyResolvedData(resolvedData);
      return;
    }

    this.loadCatalog();
  }

  ngOnDestroy(): void {
    this.availabilityChangedSubscription?.unsubscribe();
    this.catalogRequestSubscription?.unsubscribe();
    this.groupRequestSubscriptions.forEach((subscription) => subscription.unsubscribe());
  }

  protected isGroupLoading(groupId: number): boolean {
    return this.loadingGroupIds().has(groupId);
  }

  protected onPageChange(groupId: number, page: number): void {
    const group = this.catalog().find((candidate) => candidate.id === groupId);

    if (
      !group ||
      Array.isArray(group.items) ||
      page < 1 ||
      page === group.items.meta.current_page ||
      this.isGroupLoading(groupId)
    ) {
      return;
    }

    this.setGroupLoading(groupId, true);
    this.groupRequestSubscriptions.get(groupId)?.unsubscribe();

    const subscription = this.catalogService
      .withCustomLoading()
      .getFeaturedGroupItems(groupId, { page })
      .subscribe({
        next: (items) => {
          this.catalog.update((groups) =>
            groups.map((candidate) =>
              candidate.id === groupId ? { ...candidate, items } : candidate,
            ),
          );
          this.error.set(null);
        },
        error: () => {
          this.error.set(STORE_HOME_PRODUCTS_ERROR_MESSAGE);
          this.setGroupLoading(groupId, false);
        },
        complete: () => this.setGroupLoading(groupId, false),
      });

    this.groupRequestSubscriptions.set(groupId, subscription);
  }

  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 checkoutService = this.injector.get(CheckoutService);
      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 variantIds = event.variantIds ?? [];
      const directItems =
        variantIds.length > 0
          ? variantIds.map((variantId) => ({
              catalog_item_id: event.product.id,
              variant_id: variantId,
              cantidad: 1,
            }))
          : [
              {
                catalog_item_id: event.product.id,
                variant_id: event.variant ?? null,
                cantidad: event.quantity,
              },
            ];
      const purchase = await checkoutService.startCheckout(
        tenant.codigo,
        reservedCartId !== null ? { cart_id: reservedCartId } : { direct_items: directItems },
      );
      await this.router.navigate(['/checkout', purchase.id]);
    } catch (error) {
      console.error('Failed to create direct purchase:', error);

      if (error instanceof HttpErrorResponse && isExpiredStockReservationResponse(error.error)) {
        this.toastService.danger(error.error.message);
        this.cartService.loadCart(true).subscribe({
          error: (refreshError) => console.error('Error refreshing expired cart', refreshError),
        });
        return;
      }

      if (error instanceof HttpErrorResponse && isInsufficientStockResponse(error.error)) {
        const unavailableIds = error.error.unavailable_items
          .map((item) => item.variant_id)
          .filter((variantId): variantId is number => variantId !== null);

        this.unavailableVariantIds.update((current) => new Set([...current, ...unavailableIds]));
        this.toastService.danger(error.error.message);
        return;
      }

      if (error instanceof HttpErrorResponse && typeof error.error?.message === 'string') {
        this.toastService.danger(error.error.message);
        return;
      }

      this.toastService.danger('No se pudo iniciar la compra directa.');
    } 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 loadCatalog(silent = false): void {
    if (!silent) {
      this.loading.set(true);
      this.error.set(null);
    }
    this.catalogRequestSubscription?.unsubscribe();

    this.catalogRequestSubscription = this.catalogService
      .withCustomLoading()
      .getCatalog()
      .subscribe({
        next: (catalog) => {
          this.catalog.set(catalog);
          this.error.set(null);
        },
        error: () => {
          if (!silent) {
            this.catalog.set([]);
            this.error.set(STORE_HOME_PRODUCTS_ERROR_MESSAGE);
          }
          this.loading.set(false);
        },
        complete: () => this.loading.set(false),
      });
  }

  private applyResolvedData(resolvedData: StoreHomeCatalogResolvedData): void {
    this.catalog.set(resolvedData.response ?? []);
    this.loading.set(false);
    this.error.set(resolvedData.error);
  }

  private setGroupLoading(groupId: number, loading: boolean): void {
    this.loadingGroupIds.update((current) => {
      const updated = new Set(current);
      loading ? updated.add(groupId) : updated.delete(groupId);
      return updated;
    });
  }
}
