import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  input,
  model,
  output,
  signal,
} from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Subject, EMPTY } from 'rxjs';
import { catchError, debounceTime, groupBy, mergeMap, switchMap, tap } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CartItemAttribute, CartItemComponent } from '../cart-item/cart-item.component';
import { ModalService } from '../../../core/services/modal.service';
import { CartService } from '../../../core/services/cart/cart.service';
import { ToastService } from '../../../core/services/toast.service';
import { VariantSelectorVariant } from '../variant-selector/variant-selector.component';

export interface CartItemMock {
  cartItemId?: number;
  imageUrl: string | null;
  product: string;
  originalPrice: number | null;
  discountedPrice: number;
  discountPercentage: number | null;
  attributes: CartItemAttribute[];
  quantity: number;
  variantId?: number | null;
  variants?: VariantSelectorVariant[];
}

@Component({
  selector: 'app-cart',
  standalone: true,
  imports: [CartItemComponent],
  templateUrl: './cart.component.html',
  styleUrl: './cart.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CartComponent {
  private readonly cartService = inject(CartService);
  private readonly modalService = inject(ModalService);
  private readonly toastService = inject(ToastService);
  private readonly quantityUpdates$ = new Subject<{ cartItemId: number; quantity: number }>();

  readonly title = input<string>('CARRITO');
  readonly showClose = input<boolean>(false);
  readonly items = input<CartItemMock[]>([]);
  readonly subtotal = input<number>(0);
  readonly discount = input<number>(0);
  readonly total = input<number>(0);
  readonly backgroundColor = input<string>('#ffffff');
  readonly readonly = input<boolean>(false);
  readonly allowUpdateQuantity = input<boolean>(true);
  readonly allowModify = input<boolean>(true);
  readonly showModifyWhenReadonly = input<boolean>(false);
  readonly modifyAsAction = input<boolean>(false);
  readonly requireEditingMode = input<boolean>(false);
  readonly allowUpdateVariant = input<boolean>(true);
  readonly allowDelete = input<boolean>(true);
  readonly persistQuantityChanges = input<boolean>(true);
  readonly persistVariantChanges = input<boolean>(true);
  readonly persistDeleteChanges = input<boolean>(true);
  readonly editingDisabled = input<boolean>(false);
  readonly editing = model<boolean>(false);

  readonly closed = output<void>();
  readonly modify = output<void>();
  readonly itemQuantityChange = output<{
    item: CartItemMock;
    index: number;
    quantity: number;
  }>();
  readonly itemVariantChange = output<{
    item: CartItemMock;
    index: number;
    variantId: number;
  }>();
  readonly itemRemove = output<{ item: CartItemMock; index: number }>();

  protected readonly quantityOverrides = signal<Record<number, number>>({});
  protected readonly variantOverrides = signal<Record<number, number>>({});
  constructor() {
    this.quantityUpdates$
      .pipe(
        groupBy((update) => update.cartItemId),
        mergeMap((group$) =>
          group$.pipe(
            debounceTime(1000),
            switchMap((update) =>
              this.cartService.updateItemQuantity(update.cartItemId, update.quantity).pipe(
                tap({
                  next: (res) => {
                    const msg = res.message || 'Cantidad de producto actualizada.';
                    this.toastService.success(msg);
                    this.clearOverride(update.cartItemId);
                  },
                  error: (err: HttpErrorResponse) => {
                    this.clearOverride(update.cartItemId);
                    this.handleMutationError(
                      err,
                      'Error al actualizar la cantidad del producto.',
                      'Error updating cart quantity',
                    );
                  },
                }),
                catchError(() => EMPTY),
              ),
            ),
          ),
        ),
        takeUntilDestroyed(),
      )
      .subscribe();
  }

  protected getItemQuantity(item: CartItemMock): number {
    if (item.cartItemId !== undefined && this.quantityOverrides()[item.cartItemId] !== undefined) {
      return this.quantityOverrides()[item.cartItemId];
    }
    return item.quantity;
  }

  protected getItemVariant(item: CartItemMock): number | null {
    if (item.cartItemId !== undefined && this.variantOverrides()[item.cartItemId] !== undefined) {
      return this.variantOverrides()[item.cartItemId];
    }
    return item.variantId ?? null;
  }

  private clearOverride(cartItemId: number): void {
    this.quantityOverrides.update((overrides) => {
      const copy = { ...overrides };
      delete copy[cartItemId];
      return copy;
    });
  }

  protected onItemQuantityChange(index: number, newQuantity: number): void {
    if (!this.allowUpdateQuantity()) {
      return;
    }

    const mockItem = this.items()[index];
    if (!mockItem) {
      return;
    }

    this.itemQuantityChange.emit({
      item: mockItem,
      index,
      quantity: newQuantity,
    });

    if (!this.persistQuantityChanges()) {
      return;
    }

    const cartItemId = mockItem?.cartItemId;
    if (cartItemId) {
      this.quantityOverrides.update((overrides) => ({
        ...overrides,
        [cartItemId]: newQuantity,
      }));
      this.quantityUpdates$.next({
        cartItemId,
        quantity: newQuantity,
      });
    } else {
      const item = this.cartService.cart()?.items[index];
      if (item) {
        this.quantityOverrides.update((overrides) => ({
          ...overrides,
          [item.id]: newQuantity,
        }));
        this.quantityUpdates$.next({
          cartItemId: item.id,
          quantity: newQuantity,
        });
      }
    }
  }

  protected onItemVariantChange(index: number, variantId: number): void {
    if (this.readonly() || !this.allowUpdateVariant() || this.editingDisabled()) return;

    const item = this.items()[index];
    const cartItemId = item?.cartItemId;

    if (!item || !cartItemId || variantId === this.getItemVariant(item)) return;

    this.itemVariantChange.emit({ item, index, variantId });

    if (!this.persistVariantChanges()) {
      return;
    }

    this.variantOverrides.update((overrides) => ({ ...overrides, [cartItemId]: variantId }));
    this.cartService
      .updateItemVariant(cartItemId, this.getItemQuantity(item), variantId)
      .subscribe({
        next: (response) => {
          this.clearVariantOverride(cartItemId);
          this.toastService.success(response.message || 'Variante actualizada.');
        },
        error: (error: HttpErrorResponse) => {
          this.clearVariantOverride(cartItemId);
          this.handleMutationError(
            error,
            'No se pudo actualizar la variante.',
            'Error updating cart item variant',
          );
        },
      });
  }

  private clearVariantOverride(cartItemId: number): void {
    this.variantOverrides.update((overrides) => {
      const copy = { ...overrides };
      delete copy[cartItemId];
      return copy;
    });
  }

  protected onItemRemove(index: number): void {
    if (this.readonly() || !this.allowDelete() || this.editingDisabled()) return;

    const target = this.resolveRemoveTarget(index);

    if (!target) {
      return;
    }

    this.modalService
      .openConfirmDelete({
        title: 'Eliminar producto',
        content: `Se eliminará “${target.productName}” del carrito. Esta acción no se puede deshacer.`,
        confirmLabel: 'Eliminar',
        cancelLabel: 'Cancelar',
      })
      .subscribe((confirmed) => {
        if (confirmed) {
          const item = this.items()[index];
          if (item) {
            this.itemRemove.emit({ item, index });
          }

          if (this.persistDeleteChanges()) {
            this.removeItem(target.cartItemId);
          }
        }
      });
  }

  protected readonly formattedSubtotal = computed(() => this.formatCurrency(this.subtotal()));
  protected readonly formattedDiscount = computed(() => this.formatCurrency(this.discount()));
  protected readonly formattedTotal = computed(() => this.formatCurrency(this.total()));

  protected toggleEditing(): void {
    if (!this.allowModify() || this.editingDisabled()) {
      return;
    }

    if (this.modifyAsAction()) {
      this.modify.emit();
      return;
    }

    const editing = !this.editing();
    this.editing.set(editing);
  }

  private formatCurrency(value: number): string {
    const rounded = Math.round(value);
    const parts = rounded.toString().split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
    return `$ ${parts.join(',')}`;
  }

  private resolveRemoveTarget(index: number): { cartItemId: number; productName: string } | null {
    const mockItem = this.items()[index];
    const cartItemId = mockItem?.cartItemId;

    if (cartItemId) {
      return {
        cartItemId,
        productName: mockItem.product,
      };
    }

    const item = this.cartService.cart()?.items[index];

    if (!item) {
      return null;
    }

    return {
      cartItemId: item.id,
      productName: item.nombre ?? 'este producto',
    };
  }

  private removeItem(cartItemId: number): void {
    this.cartService.removeItem(cartItemId).subscribe({
      next: (res) => {
        const msg = res.message || 'Producto eliminado del carrito.';
        this.toastService.info(msg);
      },
      error: (err: HttpErrorResponse) => {
        this.handleMutationError(
          err,
          'Error al eliminar el producto del carrito.',
          'Error removing item from cart',
        );
      },
    });
  }

  private handleMutationError(
    error: HttpErrorResponse,
    fallbackMessage: string,
    logMessage: string,
  ): void {
    console.error(logMessage, error);
    this.toastService.danger(error.error?.message || fallbackMessage);

    if (error.error?.code !== 'stock_reservation.expired') {
      return;
    }

    this.cartService.loadCart(true).subscribe({
      error: (refreshError) => console.error('Error refreshing expired cart', refreshError),
    });
  }
}
