import { Injectable, Inject, BadRequestException, NotFoundException } from "@nestjs/common";
import {
  subscriptionListSchema,
  subscriptionStatusSchema,
  subscriptionStatsSchema,
} from "@customer-portal/domain/subscriptions";
import type {
  Subscription,
  SubscriptionList,
  SubscriptionStatus,
} from "@customer-portal/domain/subscriptions";
import { WhmcsConnectionFacade } from "@bff/integrations/whmcs/facades/whmcs.facade.js";
import { WhmcsClientService } from "@bff/integrations/whmcs/services/whmcs-client.service.js";
import { WhmcsSubscriptionService } from "@bff/integrations/whmcs/services/whmcs-subscription.service.js";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
import { Logger } from "nestjs-pino";
import { safeOperation, OperationCriticality } from "@bff/core/utils/safe-operation.util.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";

export interface GetSubscriptionsOptions {
  status?: SubscriptionStatus;
}

/**
 * Subscriptions Orchestrator
 *
 * Coordinates subscription management operations across multiple
 * integration services (WHMCS, Salesforce).
 */
@Injectable()
export class SubscriptionsOrchestrator {
  constructor(
    private readonly whmcsSubscriptionService: WhmcsSubscriptionService,
    private readonly whmcsClientService: WhmcsClientService,
    private readonly whmcsConnectionService: WhmcsConnectionFacade,
    private readonly mappingsService: MappingsService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Get all subscriptions for a user
   */
  async getSubscriptions(
    userId: string,
    options: GetSubscriptionsOptions = {}
  ): Promise<SubscriptionList> {
    const { status } = options;
    const whmcsClientId = await this.mappingsService.getWhmcsClientIdOrThrow(userId);

    return safeOperation(
      async () => {
        const subscriptionList = await this.whmcsSubscriptionService.getSubscriptions(
          whmcsClientId,
          userId,
          status === undefined ? {} : { status }
        );

        const parsed = subscriptionListSchema.parse(subscriptionList);

        let subscriptions = parsed.subscriptions;
        if (status) {
          const normalizedStatus = subscriptionStatusSchema.parse(status);
          subscriptions = subscriptions.filter(sub => sub.status === normalizedStatus);
        }

        return subscriptionListSchema.parse({
          subscriptions,
          totalCount: subscriptions.length,
        });
      },
      {
        criticality: OperationCriticality.CRITICAL,
        context: `Get subscriptions for user ${userId}`,
        logger: this.logger,
        rethrow: [NotFoundException, BadRequestException],
        fallbackMessage: "Failed to retrieve subscriptions",
      }
    );
  }

  /**
   * Get individual subscription by ID
   */
  async getSubscriptionById(userId: string, subscriptionId: number): Promise<Subscription> {
    // Validate subscription ID
    if (!subscriptionId || subscriptionId < 1) {
      throw new BadRequestException("Subscription ID must be a positive number");
    }

    // Get WHMCS client ID from user mapping
    const whmcsClientId = await this.mappingsService.getWhmcsClientIdOrThrow(userId);

    return safeOperation(
      async () => {
        const subscription = await this.whmcsSubscriptionService.getSubscriptionById(
          whmcsClientId,
          userId,
          subscriptionId
        );

        this.logger.log(`Retrieved subscription ${subscriptionId} for user ${userId}`, {
          productName: subscription.productName,
          status: subscription.status,
          amount: subscription.amount,
          currency: subscription.currency,
        });

        return subscription;
      },
      {
        criticality: OperationCriticality.CRITICAL,
        context: `Get subscription ${subscriptionId} for user ${userId}`,
        logger: this.logger,
        rethrow: [NotFoundException, BadRequestException],
        fallbackMessage: "Failed to retrieve subscription",
      }
    );
  }

  /**
   * Get active subscriptions for a user
   */
  async getActiveSubscriptions(userId: string): Promise<Subscription[]> {
    try {
      const subscriptionList = await this.getSubscriptions(userId, {
        status: "Active",
      });
      return subscriptionList.subscriptions;
    } catch (error) {
      this.logger.error(`Failed to get active subscriptions for user ${userId}`, {
        error: extractErrorMessage(error),
      });
      throw error;
    }
  }

  /**
   * Get subscriptions by status
   */
  async getSubscriptionsByStatus(
    userId: string,
    status: SubscriptionStatus
  ): Promise<Subscription[]> {
    try {
      const normalizedStatus = subscriptionStatusSchema.parse(status);
      const subscriptionList = await this.getSubscriptions(userId, { status: normalizedStatus });
      return subscriptionList.subscriptions;
    } catch (error) {
      this.logger.error(`Failed to get ${status} subscriptions for user ${userId}`, {
        error: extractErrorMessage(error),
      });
      throw error;
    }
  }

  /**
   * Get subscription statistics for a user
   */
  async getSubscriptionStats(userId: string): Promise<{
    total: number;
    active: number;
    completed: number;
    cancelled: number;
  }> {
    return safeOperation(
      async () => {
        const subscriptionList = await this.getSubscriptions(userId);
        const subscriptions: Subscription[] = subscriptionList.subscriptions;

        const stats = {
          total: subscriptions.length,
          active: subscriptions.filter(s => s.status === "Active").length,
          completed: subscriptions.filter(s => s.status === "Completed").length,
          cancelled: subscriptions.filter(s => s.status === "Cancelled").length,
        };

        this.logger.log(`Generated subscription stats for user ${userId}`, stats);

        return subscriptionStatsSchema.parse(stats);
      },
      {
        criticality: OperationCriticality.CRITICAL,
        context: `Generate subscription stats for user ${userId}`,
        logger: this.logger,
        rethrow: [NotFoundException, BadRequestException],
      }
    );
  }

  /**
   * Get subscriptions expiring soon (within next 30 days)
   */
  async getExpiringSoon(userId: string, days: number = 30): Promise<Subscription[]> {
    return safeOperation(
      async () => {
        const subscriptionList = await this.getSubscriptions(userId);
        const subscriptions: Subscription[] = subscriptionList.subscriptions;

        const cutoffDate = new Date();
        cutoffDate.setDate(cutoffDate.getDate() + days);

        const expiringSoon = subscriptions.filter(subscription => {
          if (!subscription.nextDue || subscription.status !== "Active") {
            return false;
          }

          const nextDueDate = new Date(subscription.nextDue);
          return nextDueDate <= cutoffDate;
        });

        this.logger.log(
          `Found ${expiringSoon.length} subscriptions expiring within ${days} days for user ${userId}`
        );
        return expiringSoon;
      },
      {
        criticality: OperationCriticality.CRITICAL,
        context: `Get expiring subscriptions for user ${userId}`,
        logger: this.logger,
        rethrow: [NotFoundException, BadRequestException],
      }
    );
  }

  /**
   * Get recent subscription activity (newly created or status changed)
   */
  async getRecentActivity(userId: string, days: number = 30): Promise<Subscription[]> {
    return safeOperation(
      async () => {
        const subscriptionList = await this.getSubscriptions(userId);
        const subscriptions = subscriptionList.subscriptions;

        const cutoffDate = new Date();
        cutoffDate.setDate(cutoffDate.getDate() - days);

        const recentActivity = subscriptions.filter((subscription: Subscription) => {
          const registrationDate = new Date(subscription.registrationDate);
          return registrationDate >= cutoffDate;
        });

        this.logger.log(
          `Found ${recentActivity.length} recent subscription activities within ${days} days for user ${userId}`
        );
        return recentActivity;
      },
      {
        criticality: OperationCriticality.CRITICAL,
        context: `Get recent subscription activity for user ${userId}`,
        logger: this.logger,
        rethrow: [NotFoundException, BadRequestException],
      }
    );
  }

  /**
   * Search subscriptions by product name or domain
   */
  async searchSubscriptions(userId: string, query: string): Promise<Subscription[]> {
    if (!query || query.trim().length < 2) {
      throw new BadRequestException("Search query must be at least 2 characters long");
    }

    return safeOperation(
      async () => {
        const subscriptionList = await this.getSubscriptions(userId);
        const subscriptions = subscriptionList.subscriptions;

        const searchTerm = query.toLowerCase().trim();
        const matches = subscriptions.filter((subscription: Subscription) => {
          const productName = subscription.productName.toLowerCase();
          const domain = subscription.domain?.toLowerCase() || "";

          return productName.includes(searchTerm) || domain.includes(searchTerm);
        });

        this.logger.log(
          `Found ${matches.length} subscriptions matching query "${query}" for user ${userId}`
        );
        return matches;
      },
      {
        criticality: OperationCriticality.CRITICAL,
        context: `Search subscriptions for user ${userId}`,
        logger: this.logger,
        rethrow: [NotFoundException, BadRequestException],
      }
    );
  }

  /**
   * Invalidate subscription cache for a user
   */
  async invalidateCache(userId: string, subscriptionId?: number): Promise<void> {
    try {
      if (subscriptionId) {
        await this.whmcsSubscriptionService.invalidateSubscriptionCache(userId, subscriptionId);
      } else {
        await this.whmcsClientService.invalidateUserCache(userId);
      }

      const subscriptionSuffix = subscriptionId ? `, subscription ${subscriptionId}` : "";
      this.logger.log(`Invalidated subscription cache for user ${userId}${subscriptionSuffix}`);
    } catch (error) {
      this.logger.error(`Failed to invalidate subscription cache for user ${userId}`, {
        error: extractErrorMessage(error),
        subscriptionId,
      });
    }
  }

  /**
   * Health check for subscription service
   */
  async healthCheck(): Promise<{ status: string; details: unknown }> {
    try {
      const whmcsHealthy = await this.whmcsConnectionService.healthCheck();

      return {
        status: whmcsHealthy ? "healthy" : "unhealthy",
        details: {
          whmcsApi: whmcsHealthy ? "connected" : "disconnected",
          timestamp: new Date().toISOString(),
        },
      };
    } catch (error) {
      this.logger.error("Subscription service health check failed", {
        error: extractErrorMessage(error),
      });
      return {
        status: "unhealthy",
        details: {
          error: extractErrorMessage(error),
          timestamp: new Date().toISOString(),
        },
      };
    }
  }
}
