import { inject, Injectable } from '@angular/core';
import { firstValueFrom } from 'rxjs';

import { environment } from '../../../environments/environment';
import { ApiPaginatedResponse } from './api-paginated-response.interface';
import { ApiPaginationQueryParams } from './api-pagination-query-params.interface';
import { BaseApiService } from './base-api.service';
import { CatalogAvailabilityService } from './catalog/catalog-availability.service';

export interface UpdatePurchaseCustomerPayload {
  dni: string;
  telefono: string;
  nombre_apellido: string;
  email: string;
}

export interface DirectCheckoutItem {
  catalog_item_id: number;
  variant_id: number | null;
  cantidad: number;
}

export interface UnavailableCheckoutItem {
  index: number;
  catalog_item_id: number;
  variant_id: number | null;
  requested_quantity: number;
  available_quantity: number;
  message: string;
}

export interface InsufficientStockResponse {
  code: 'purchase.insufficient_stock';
  message: string;
  errors: Record<string, string[]>;
  unavailable_items: UnavailableCheckoutItem[];
}

export function isInsufficientStockResponse(value: unknown): value is InsufficientStockResponse {
  if (typeof value !== 'object' || value === null) return false;

  const response = value as Partial<InsufficientStockResponse>;

  return (
    response.code === 'purchase.insufficient_stock' && Array.isArray(response.unavailable_items)
  );
}

export interface ExpiredStockReservationResponse {
  code: 'stock_reservation.expired';
  message: string;
}

export function isExpiredStockReservationResponse(
  value: unknown,
): value is ExpiredStockReservationResponse {
  return (
    typeof value === 'object' &&
    value !== null &&
    (value as Partial<ExpiredStockReservationResponse>).code === 'stock_reservation.expired'
  );
}

export type StartCheckoutPayload =
  | {
      cart_id: number;
    }
  | {
      direct_items: DirectCheckoutItem[];
    };

export interface PurchaseStatusResponse {
  status: string | null;
  expires_at: string | null;
  expires_in_seconds: number | null;
  server_time: string;
  payment_verification?: PurchasePaymentVerificationResponse;
}

export type PurchasePaymentCandidateReason =
  | 'ambiguous_exact_match'
  | 'exact_dni_near_amount'
  | 'exact_amount_near_dni';

export interface PurchasePaymentCandidatePrimaryResponse {
  reason: PurchasePaymentCandidateReason;
  dni_distance: number | null;
  payment_amount: string;
  purchase_amount: string;
  amount_difference: string;
  confidence: 'exact' | 'high' | 'medium';
  detected_at: string | null;
}

export interface PurchasePaymentVerificationResponse {
  status: 'pending' | 'candidate';
  candidate_count: number;
  primary: PurchasePaymentCandidatePrimaryResponse | null;
  reasons: PurchasePaymentCandidateReason[];
}

export interface PurchaseSummaryResponse extends PurchaseStatusResponse {
  id: number;
  created_at: string | null;
  total: string;
}

export interface PurchaseDetailAttributeResponse {
  name: string;
  value: string | number | boolean | null;
}

export interface PurchaseDetailItemResponse {
  id: number;
  quantity: number;
  unit_price: string;
  line_total: string;
  source_catalog_item_id: number | null;
  source_variant_id: number | null;
  item_details: {
    nombre: string;
    descripcion: string | null;
    slug: string;
    imagen: string | null;
    attributes: PurchaseDetailAttributeResponse[];
  };
}

export interface PurchaseDetailResponse extends PurchaseStatusResponse {
  id: number;
  cart_id: number | null;
  tenant_codigo: string;
  user_id: number;
  created_at: string | null;
  payment_method: string | null;
  dni: string | null;
  transfer_payer_dni: string | null;
  telefono: string | null;
  nombre_apellido: string | null;
  email: string | null;
  items_source: 'purchase';
  items: PurchaseDetailItemResponse[];
  tickets_count?: number;
  has_generated_tickets?: boolean;
  subtotal: string;
  total: string;
}

@Injectable({
  providedIn: 'root',
})
export class CheckoutService extends BaseApiService {
  private readonly catalogAvailabilityService = inject(CatalogAvailabilityService);

  async startCheckout(
    tenantCode: string,
    payload: StartCheckoutPayload,
  ): Promise<PurchaseDetailResponse> {
    try {
      const response = await firstValueFrom(
        this.http.post<{ data?: PurchaseDetailResponse } | PurchaseDetailResponse>(
          `${environment.url}tenants/${tenantCode}/compras/start-checkout`,
          payload,
        ),
      );

      const purchase = this.extractResponseData<PurchaseDetailResponse>(response);

      if (!purchase?.id) {
        throw new Error('Error al crear la compra.');
      }

      return purchase;
    } catch (error) {
      const responseBody = (error as { error?: unknown } | null)?.error;
      if (!isExpiredStockReservationResponse(responseBody)) {
        this.catalogAvailabilityService.notifyAvailabilityChanged();
      }
      throw error;
    }
  }

  async generatePaymentIntent(
    tenantCode: string,
    purchaseId: number,
    method: 'qr' | 'transfer',
    payerDni?: string,
  ): Promise<any> {
    const payload: any = { method };
    if (payerDni) {
      payload.transfer_payer_dni = payerDni;
    }
    const response = await firstValueFrom(
      this.http.post<any>(
        `${environment.url}tenants/${tenantCode}/compras/${purchaseId}/payment-intent`,
        payload,
      ),
    );
    if (!response) {
      throw new Error('Error al generar la intención de pago.');
    }
    return response;
  }

  async updateCustomerData(
    tenantCode: string,
    purchaseId: number,
    payload: UpdatePurchaseCustomerPayload,
  ): Promise<PurchaseDetailResponse> {
    const response = await firstValueFrom(
      this.http.patch<{ data?: PurchaseDetailResponse } | PurchaseDetailResponse>(
        `${environment.url}tenants/${tenantCode}/compras/${purchaseId}/customer-data`,
        payload,
      ),
    );

    const purchase = this.extractResponseData<PurchaseDetailResponse>(response);
    if (!purchase) {
      throw new Error('Error al actualizar los datos de la compra.');
    }

    return purchase;
  }

  async submitPurchaseForReview(
    tenantCode: string,
    purchaseId: number,
  ): Promise<PurchaseStatusResponse> {
    const response = await firstValueFrom(
      this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>(
        `${environment.url}tenants/${tenantCode}/compras/${purchaseId}/review`,
        {},
      ),
    );

    const purchase = this.extractResponseData<PurchaseStatusResponse>(response);
    if (!purchase) {
      throw new Error('Error al enviar la compra a revisi\u00f3n.');
    }

    return purchase;
  }

  async cancelPurchase(tenantCode: string, purchaseId: number): Promise<PurchaseStatusResponse> {
    const response = await firstValueFrom(
      this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>(
        `${environment.url}tenants/${tenantCode}/compras/${purchaseId}/cancel`,
        {},
      ),
    );

    const purchase = this.extractResponseData<PurchaseStatusResponse>(response);
    if (!purchase) {
      throw new Error('Error al cancelar la compra.');
    }

    return purchase;
  }

  async getPurchases(
    tenantCode: string,
    status?: string,
    pagination: ApiPaginationQueryParams = {},
  ): Promise<ApiPaginatedResponse<PurchaseSummaryResponse[]>> {
    const params: Record<string, string | number> = {};

    if (status) params['status'] = status;
    if (pagination.page) params['page'] = pagination.page;
    if (pagination.per_page) params['per_page'] = pagination.per_page;

    const response = await firstValueFrom(
      this.http.get<ApiPaginatedResponse<PurchaseSummaryResponse[]>>(
        `${environment.url}tenants/${tenantCode}/compras`,
        { params },
      ),
    );
    if (!response) {
      throw new Error('Error al obtener las compras.');
    }
    return response;
  }

  async getPurchase(
    tenantCode: string,
    purchaseId: string | number,
  ): Promise<PurchaseDetailResponse> {
    const response = await firstValueFrom(
      this.http.get<{ data?: PurchaseDetailResponse } | PurchaseDetailResponse>(
        `${environment.url}tenants/${tenantCode}/compras/${purchaseId}`,
      ),
    );

    const purchase = this.extractResponseData<PurchaseDetailResponse>(response);

    if (!purchase) {
      throw new Error('Error al obtener la compra.');
    }

    return purchase;
  }

  private extractResponseData<T>(response: T | { data?: T } | null | undefined): T | null {
    if (!response) {
      return null;
    }

    if (typeof response === 'object' && 'data' in response && response.data) {
      return response.data;
    }

    return response as T;
  }
}
