import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  OnDestroy,
  OnInit,
  computed,
  effect,
  inject,
  signal,
  viewChild,
  PLATFORM_ID,
} from '@angular/core';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { HttpErrorResponse } from '@angular/common/http';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { Subscription } from 'rxjs';

import { CatalogService } from '../../../../core/services/catalog/catalog.service';
import { CatalogAvailabilityService } from '../../../../core/services/catalog/catalog-availability.service';
import { ToastService } from '../../../../core/services/toast.service';
import { CartService } from '../../../../core/services/cart/cart.service';
import {
  CatalogItemDetail,
  CatalogItemVariant,
} from '../../../../core/services/catalog/catalog.interface';
import { ProductCarouselComponent } from '../../components/product-carousel/product-carousel.component';
import { ButtonComponent } from '../../../../shared/components/button/button.component';
import { ProductAttributeSelectorComponent } from '../../components/product-attribute-selector/product-attribute-selector.component';
import { QuantitySelectorComponent } from '../../../../shared/components/quantity-selector/quantity-selector.component';
import { ProductDetailResolvedData } from './product-detail-page.resolver';
import { CheckoutService } from '../../../../core/services/checkout.service';
import { TenantService } from '../../../../core/services/tenant.service';
import { AuthService } from '../../../../core/services/auth/auth.service';

@Component({
  selector: 'app-product-detail-page',
  standalone: true,
  imports: [
    CommonModule,
    RouterModule,
    ProductCarouselComponent,
    ButtonComponent,
    ProductAttributeSelectorComponent,
    QuantitySelectorComponent,
  ],
  templateUrl: './product-detail-page.component.html',
  styleUrl: './product-detail-page.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductDetailPageComponent implements OnInit, OnDestroy {
  private readonly route = inject(ActivatedRoute);
  private readonly router = inject(Router);
  private readonly catalogService = inject(CatalogService);
  private readonly catalogAvailabilityService = inject(CatalogAvailabilityService);
  private readonly toastService = inject(ToastService);
  private readonly cartService = inject(CartService);
  private readonly checkoutService = inject(CheckoutService);
  private readonly tenantService = inject(TenantService);
  private readonly authService = inject(AuthService);
  private readonly attributeSelector = viewChild(ProductAttributeSelectorComponent);
  private readonly carouselHost = viewChild<ElementRef<HTMLElement>>('carouselHost');
  private readonly descriptionBody = viewChild<ElementRef<HTMLElement>>('descriptionBody');
  private readonly platformId = inject(PLATFORM_ID);
  private readonly isBrowser = isPlatformBrowser(this.platformId);

  private routeSub: Subscription | null = null;
  private productSub: Subscription | null = null;
  private availabilityChangedSub: Subscription | null = null;
  private carouselResizeObserver: ResizeObserver | null = null;
  private observedCarouselPreview: HTMLElement | null = null;
  private measurementTimer: ReturnType<typeof setTimeout> | null = null;

  protected readonly product = signal<CatalogItemDetail | null>(null);
  protected readonly carouselImages = computed(() => {
    const prod = this.product();
    if (!prod) return [];
    if (prod.selected_variant?.images && prod.selected_variant.images.length > 0) {
      return prod.selected_variant.images;
    }
    if (prod.images && prod.images.length > 0) {
      return prod.images;
    }
    return [];
  });
  protected readonly loading = signal(false);
  protected readonly variantLoading = signal(false);
  protected readonly addingToCart = signal(false);
  protected readonly creatingDirectPurchase = signal(false);
  protected readonly error = signal<string | null>(null);
  protected readonly selectedVariant = signal<CatalogItemVariant | null>(null);
  protected readonly effectiveDescription = computed(
    () => this.selectedVariant()?.descripcion ?? this.product()?.descripcion ?? '',
  );
  protected readonly effectivePrice = computed(
    () => this.selectedVariant()?.precio ?? this.product()?.precio,
  );
  protected readonly quantity = signal(1);
  protected readonly selectedVariantMax = computed<number | null>(() => {
    const prod = this.product();
    const variant = this.selectedVariant();
    if (!prod) return 0;

    return variant
      ? (variant.maximum_addable_quantity ?? null)
      : prod.variants.length === 0
        ? (prod.maximum_addable_quantity ?? null)
        : 0;
  });
  protected readonly selectedVariantAvailable = computed(() => {
    const prod = this.product();
    if (!prod) return false;
    if (this.selectedVariantMax() === 0) return false;

    const variant = this.selectedVariant();
    if (variant) return this.isVariantAvailable(variant);
    if (prod.purpose === 'entry') return false;
    if (prod.variants.length > 0) return false;

    return this.selectedVariantMax() !== 0;
  });
  protected readonly descriptionExpanded = signal(false);
  protected readonly descriptionMaxHeight = signal(0);
  protected readonly descriptionHasOverflow = signal(false);
  protected readonly renderableAttributes = computed(() =>
    (this.product()?.attributes ?? []).filter(
      (attribute) => attribute.show_in_selector !== false && attribute.options.length > 0,
    ),
  );
  protected readonly hasRenderableAttributes = computed(
    () => this.renderableAttributes().length > 0,
  );
  protected readonly oldPrice = computed<string | null>(() => null);
  protected readonly showDescriptionToggle = computed(
    () => this.descriptionExpanded() || this.descriptionHasOverflow(),
  );

  constructor() {
    effect(() => {
      this.carouselHost();
      this.descriptionBody();
      this.product();
      this.selectedVariant();
      this.descriptionExpanded();

      if (this.isBrowser) {
        queueMicrotask(() => {
          this.bindCarouselResizeObserver();
          this.refreshMeasurements();
        });
      }
    });
  }

  ngOnInit(): void {
    this.availabilityChangedSub = this.catalogAvailabilityService.availabilityChanged$.subscribe(
      () => this.refreshProductAvailability(),
    );

    this.routeSub = this.route.data.subscribe((data) => {
      const resolvedData = data['productDetailData'] as ProductDetailResolvedData | undefined;

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

  ngOnDestroy(): void {
    this.availabilityChangedSub?.unsubscribe();
    this.routeSub?.unsubscribe();
    this.productSub?.unsubscribe();
    this.carouselResizeObserver?.disconnect();
    this.clearMeasurementTimer();
  }

  private loadProductVariant(productId: number, variantId: number): void {
    this.variantLoading.set(true);

    this.productSub?.unsubscribe();
    this.productSub = this.catalogService
      .withCustomLoading()
      .getCatalogItem(productId, variantId)
      .subscribe({
        next: (prod) => {
          this.applyProduct(prod, false);
          this.variantLoading.set(false);
        },
        error: (err: HttpErrorResponse) => {
          const errorMessage = err.error?.message || 'No pudimos cargar la variante seleccionada.';
          this.toastService.danger(errorMessage);
          this.variantLoading.set(false);

          this.product.update((currentProduct) => {
            if (!currentProduct) return null;
            return {
              ...currentProduct,
              variants: currentProduct.variants.filter((variant) => variant.id !== variantId),
            };
          });

          this.attributeSelector()?.reset();
        },
      });
  }

  private refreshProductAvailability(): void {
    const currentProduct = this.product();
    if (!currentProduct) {
      return;
    }

    const variantId = this.selectedVariant()?.id;
    this.productSub?.unsubscribe();
    this.productSub = this.catalogService
      .withCustomLoading()
      .getCatalogItem(currentProduct.id, variantId)
      .subscribe({
        next: (product) => {
          this.applyProduct(product, false);

          const maximum = this.selectedVariantMax();
          if (maximum !== null && this.quantity() > maximum) {
            this.quantity.set(Math.max(1, maximum));
          }
        },
        error: () => {
          // Keep the last known availability if the silent refresh fails.
        },
      });
  }

  private applyResolvedData(resolvedData: ProductDetailResolvedData): void {
    this.productSub?.unsubscribe();
    this.loading.set(false);
    this.variantLoading.set(false);

    if (resolvedData.error) {
      this.product.set(null);
      this.error.set(resolvedData.error);
      return;
    }

    if (resolvedData.product) {
      this.applyProduct(resolvedData.product, true);
    }
  }

  private applyProduct(prod: CatalogItemDetail, resetQuantity: boolean): void {
    this.product.set(prod);
    const matchingVariant =
      prod.variants.find((variant) => variant.id === prod.selected_variant?.id) || null;
    this.selectedVariant.set(matchingVariant);
    if (resetQuantity) {
      this.quantity.set(1);
    }
    this.descriptionExpanded.set(false);
    this.descriptionHasOverflow.set(false);
    this.error.set(null);
  }

  protected goBack(): void {
    this.router.navigate(['/']);
  }

  protected getFormattedPrice(priceStr: string | undefined): string {
    if (!priceStr) return '$0';
    const parsed = Number(priceStr);
    const value = Number.isFinite(parsed) ? parsed : 0;
    const rounded = Math.round(value);
    const parts = rounded.toString().split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
    return `$${parts.join(',')}`;
  }

  protected onVariantChange(variant: CatalogItemVariant | null): void {
    if (this.selectedVariant()?.id === variant?.id) {
      return;
    }

    this.selectedVariant.set(variant);
    this.descriptionExpanded.set(false);
    this.descriptionHasOverflow.set(false);
    const maximum = this.selectedVariantMax();
    if (maximum !== null && this.quantity() > maximum) {
      this.quantity.set(Math.max(1, maximum));
    }

    const currentProduct = this.product();
    if (variant && currentProduct && currentProduct.selected_variant?.id !== variant.id) {
      this.loadProductVariant(currentProduct.id, variant.id);
    }
  }

  protected addToCart(): void {
    const currentProduct = this.product();
    const variant = this.selectedVariant();
    if (!currentProduct || !this.selectedVariantAvailable()) {
      this.toastService.danger('Por favor, selecciona una variante.');
      return;
    }

    this.addingToCart.set(true);
    this.cartService
      .withCustomLoading()
      .addItem(currentProduct.id, variant?.id ?? null, this.quantity())
      .subscribe({
        next: (res) => {
          const msg = res.message || 'Producto agregado al carrito';
          this.toastService.success(msg);
          this.addingToCart.set(false);
        },
        error: (err: HttpErrorResponse) => {
          const errorMessage = err.error?.message || 'No se pudo agregar el producto al carrito.';
          this.toastService.danger(errorMessage);
          this.addingToCart.set(false);
        },
      });
  }

  protected async buyNow(): Promise<void> {
    const currentProduct = this.product();
    const variant = this.selectedVariant();

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

    if (!currentProduct || !this.selectedVariantAvailable()) {
      this.toastService.danger('Por favor, selecciona una variante.');
      return;
    }

    const tenant = this.tenantService.tenant();
    const user = this.authService.user();

    if (!user) {
      void this.router.navigate(['/login'], {
        queryParams: { returnUrl: `/producto/${currentProduct.id}` },
      });
      return;
    }

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

    this.creatingDirectPurchase.set(true);
    try {
      const purchase = await this.checkoutService.startCheckout(tenant.codigo, {
        direct_items: [
          {
            catalog_item_id: currentProduct.id,
            variant_id: variant?.id ?? null,
            cantidad: this.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);
    } finally {
      this.creatingDirectPurchase.set(false);
    }
  }

  private isVariantAvailable(variant: CatalogItemVariant): boolean {
    return variant.maximum_addable_quantity !== 0;
  }

  protected toggleDescription(): void {
    this.descriptionExpanded.update((current) => !current);
  }

  private bindCarouselResizeObserver(): void {
    const previewElement = this.getCarouselPreviewElement();

    if (!previewElement || previewElement === this.observedCarouselPreview) {
      return;
    }

    this.carouselResizeObserver?.disconnect();
    this.observedCarouselPreview = previewElement;

    if (typeof ResizeObserver === 'undefined') {
      this.updateDescriptionMaxHeight();
      return;
    }

    this.carouselResizeObserver = new ResizeObserver(() => {
      this.scheduleMeasurement();
    });
    this.carouselResizeObserver.observe(previewElement);
    this.updateDescriptionMaxHeight();
  }

  private scheduleMeasurement(): void {
    this.clearMeasurementTimer();
    this.measurementTimer = setTimeout(() => {
      this.measurementTimer = null;
      this.refreshMeasurements();
    }, 0);
  }

  private clearMeasurementTimer(): void {
    if (this.measurementTimer) {
      clearTimeout(this.measurementTimer);
      this.measurementTimer = null;
    }
  }

  private refreshMeasurements(): void {
    this.updateDescriptionMaxHeight();
    this.updateDescriptionOverflow();
  }

  private updateDescriptionMaxHeight(): void {
    const previewElement = this.getCarouselPreviewElement();
    const nextHeight = previewElement?.getBoundingClientRect().height ?? 0;

    if (nextHeight > 0) {
      this.descriptionMaxHeight.set(nextHeight);
    }
  }

  private updateDescriptionOverflow(): void {
    const descriptionElement = this.descriptionBody()?.nativeElement;

    if (!descriptionElement || this.descriptionExpanded()) {
      return;
    }

    this.descriptionHasOverflow.set(
      descriptionElement.scrollHeight - descriptionElement.clientHeight > 1,
    );
  }

  private getCarouselPreviewElement(): HTMLElement | null {
    return (
      this.carouselHost()?.nativeElement.querySelector<HTMLElement>('.product-carousel__main') ??
      null
    );
  }
}
