import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { environment } from '@environments/environment';
import { Observable, map, catchError, throwError } from 'rxjs';

import { 
  Subscription,
  SubscriptionPayment,
  SubscriptionsResponse,
  SubscriptionPaymentsResponse,
  SearchSubscriptionsParams,
  SubscriptionStatus,
  SubscriptionPeriodicity,
  Currency
} from '@core/models/dcb-bo-hub-subscription.model';

// Interfaces pour les réponses API
export interface MessageResponse {
  message: string;
}

// ===== SERVICE SUBSCRIPTIONS =====

@Injectable({ providedIn: 'root' })
export class SubscriptionsService {
  private http = inject(HttpClient);
  private subcriptionBaseApiUrl = `${environment.apiCoreUrl}/subscriptions`;
  private paymentBaseApiUrl = `${environment.apiCoreUrl}/payments`;

  // === MÉTHODES SUBSCRIPTIONS ===

  /**
   * Récupère la liste des abonnements
   */
  getSubscriptions(params?: SearchSubscriptionsParams): Observable<SubscriptionsResponse> {
    let httpParams = new HttpParams();
    
    if (params) {
      Object.keys(params).forEach(key => {
        const value = params[key as keyof SearchSubscriptionsParams];
        if (value !== undefined && value !== null) {
          httpParams = httpParams.set(key, value.toString());
        }
      });
    }

    return this.http.get<Subscription[]>(this.subcriptionBaseApiUrl, { params: httpParams }).pipe(
      map(subscriptions => ({
        subscriptions: subscriptions.map(sub => this.mapToSubscriptionModel(sub)),
        statistics: this.calculateSubscriptionStats(subscriptions)
      })),
      catchError(error => {
        console.error('Error loading subscriptions:', error);
        return throwError(() => error);
      })
    );
  }

  /**
   * Récupère les abonnements par merchant
   */
  getSubscriptionsByMerchant(merchantId: number, params?: Omit<SearchSubscriptionsParams, 'merchantId'>): Observable<SubscriptionsResponse> {
    let httpParams = new HttpParams();
    
    if (params) {
      for (const key in params) {
        if (params.hasOwnProperty(key)) {
          const value = params[key as keyof typeof params];
          if (value !== undefined && value !== null) {
            httpParams = httpParams.set(key, value.toString());
          }
        }
      }
    }

    return this.http.get<Subscription[]>(`${this.subcriptionBaseApiUrl}/merchant/${merchantId}`, { 
      params: httpParams 
    }).pipe(
      map(subscriptions => ({
        subscriptions: subscriptions.map(sub => this.mapToSubscriptionModel(sub)),
        statistics: this.calculateSubscriptionStats(subscriptions)
      })),
      catchError(error => {
        console.error(`Error loading subscriptions for merchant ${merchantId}:`, error);
        return throwError(() => error);
      })
    );
  }

  /**
   * Récupère un abonnement par son ID
   */
  getSubscriptionById(subscriptionId: string): Observable<Subscription> {
    return this.http.get<Subscription>(`${this.subcriptionBaseApiUrl}/${subscriptionId}`).pipe(
      map(subscription => this.mapToSubscriptionModel(subscription)),
      catchError(error => {
        console.error(`Error loading subscription ${subscriptionId}:`, error);
        return throwError(() => error);
      })
    );
  }

  /**
   * Récupère les paiements d'un abonnement
   */
  getSubscriptionPayments(merchantId: string | number, subscriptionId: string | number): Observable<SubscriptionPaymentsResponse> {
    // Convertir en number si c'est une string numérique, sinon utiliser tel quel
    const merchantIdNum = typeof merchantId === 'string' ? parseInt(merchantId, 10) : merchantId;
    const subscriptionIdNum = typeof subscriptionId === 'string' ? parseInt(subscriptionId, 10) : subscriptionId;

    return this.http.get<SubscriptionPayment[]>(
      `${this.paymentBaseApiUrl}/merchant/${merchantIdNum}/subscription/${subscriptionIdNum}`
    ).pipe(
      map(payments => ({
        merchantId: merchantIdNum.toString(),
        subscriptionId: subscriptionIdNum.toString(),
        payments: payments.map(payment => this.mapToPaymentModel(payment))
      })),
      catchError(error => {
        console.error(`Error loading payments for subscription ${subscriptionId} for merchant ${merchantId}:`, error);
        return throwError(() => error);
      })
    );
  }

  /**
   * Recherche des abonnements avec filtres
   */
  searchSubscriptions(params: SearchSubscriptionsParams): Observable<Subscription[]> {
    return this.getSubscriptions(params).pipe(
      map(response => response.subscriptions)
    );
  }

  /**
   * Récupère les abonnements par statut
   */
  getSubscriptionsByStatus(status: SubscriptionStatus): Observable<Subscription[]> {
    return this.searchSubscriptions({ status });
  }

  /**
   * Récupère les abonnements par périodicité
   */
  getSubscriptionsByPeriodicity(periodicity: SubscriptionPeriodicity): Observable<Subscription[]> {
    return this.searchSubscriptions({ periodicity });
  }

  /**
   * Récupère les abonnements actifs
   */
  getActiveSubscriptions(): Observable<Subscription[]> {
    return this.getSubscriptionsByStatus(SubscriptionStatus.ACTIVE);
  }

  /**
   * Récupère les abonnements suspendus
   */
  getSuspendedSubscriptions(): Observable<Subscription[]> {
    return this.getSubscriptionsByStatus(SubscriptionStatus.SUSPENDED);
  }

  // === MÉTHODES UTILITAIRES ===

  /**
   * Calcule les statistiques des abonnements
   */
  private calculateSubscriptionStats(subscriptions: any[]): {
    total: number;
    active: number;
    suspended: number;
    cancelled: number;
    totalRevenue: number;
    averageAmount: number;
  } {
    const total = subscriptions.length;
    const active = subscriptions.filter(sub => sub.status === SubscriptionStatus.ACTIVE).length;
    const suspended = subscriptions.filter(sub => sub.status === SubscriptionStatus.SUSPENDED).length;
    const cancelled = subscriptions.filter(sub => sub.status === SubscriptionStatus.CANCELLED).length;
    const totalRevenue = subscriptions.reduce((sum, sub) => sum + sub.amount, 0);
    const averageAmount = total > 0 ? totalRevenue / total : 0;

    return {
      total,
      active,
      suspended,
      cancelled,
      totalRevenue,
      averageAmount
    };
  }

  /**
   * Mappe la réponse API vers le modèle Subscription
   */
  private mapToSubscriptionModel(apiSubscription: any): Subscription {
    return {
      id: apiSubscription.id,
      externalReference: apiSubscription.externalReference,
      periodicity: apiSubscription.periodicity as SubscriptionPeriodicity,
      startDate: apiSubscription.startDate,
      endDate: apiSubscription.endDate,
      amount: apiSubscription.amount,
      currency: apiSubscription.currency as Currency,
      token: apiSubscription.token,
      status: apiSubscription.status as SubscriptionStatus,
      nextPaymentDate: apiSubscription.nextPaymentDate,
      suspendedAt: apiSubscription.suspendedAt,
      merchantPartnerId: apiSubscription.merchantPartnerId,
      customerId: apiSubscription.customerId,
      planId: apiSubscription.planId,
      serviceId: apiSubscription.serviceId,
      failureCount: apiSubscription.failureCount,
      createdAt: apiSubscription.createdAt,
      updatedAt: apiSubscription.updatedAt,
      metadata: apiSubscription.metadata || {}
    };
  }

  /**
   * Mappe la réponse API vers le modèle Payment
   */
  private mapToPaymentModel(apiPayment: any): SubscriptionPayment {
    return {
      id: apiPayment.id,
      subscriptionId: apiPayment.subscriptionId,
      amount: apiPayment.amount,
      currency: apiPayment.currency as Currency,
      status: apiPayment.status as 'PENDING' | 'SUCCESS' | 'FAILED',
      reference: apiPayment.reference,
      description: apiPayment.description,
      metadata: apiPayment.metadata || { internatRef: '' },
      partnerId: apiPayment.partnerId,
      createdAt: apiPayment.createdAt,
      updatedAt: apiPayment.updatedAt
    };
  }

  /**
   * Valide si un statut est valide
   */
  isValidSubscriptionStatus(status: string): boolean {
    return Object.values(SubscriptionStatus).includes(status as SubscriptionStatus);
  }

  /**
   * Valide si une périodicité est valide
   */
  isValidPeriodicity(periodicity: string): boolean {
    return Object.values(SubscriptionPeriodicity).includes(periodicity as SubscriptionPeriodicity);
  }

  /**
   * Valide si une devise est valide
   */
  isValidCurrency(currency: string): boolean {
    return Object.values(Currency).includes(currency as Currency);
  }
}