import { Injectable, Inject } from "@nestjs/common";
import type { Invoice, InvoiceList } from "@customer-portal/domain/billing";
import type { Subscription, SubscriptionList } from "@customer-portal/domain/subscriptions";
import type { PaymentMethodList, PaymentGatewayList } from "@customer-portal/domain/payments";
import {
  Providers as CustomerProviders,
  addressSchema,
  type Address,
  type WhmcsClient,
} from "@customer-portal/domain/customer";
import { WhmcsConnectionOrchestratorService } from "./connection/services/whmcs-connection-orchestrator.service";
import { WhmcsInvoiceService, InvoiceFilters } from "./services/whmcs-invoice.service";
import {
  WhmcsSubscriptionService,
  SubscriptionFilters,
} from "./services/whmcs-subscription.service";
import { WhmcsClientService } from "./services/whmcs-client.service";
import { WhmcsPaymentService } from "./services/whmcs-payment.service";
import { WhmcsSsoService } from "./services/whmcs-sso.service";
import { WhmcsOrderService } from "./services/whmcs-order.service";
import type { WhmcsAddClientParams, WhmcsClientResponse } from "@customer-portal/domain/customer";
import type { WhmcsGetClientsProductsParams } from "@customer-portal/domain/subscriptions";
import type { WhmcsProductListResponse } from "@customer-portal/domain/subscriptions";
import type { WhmcsCatalogProductNormalized } from "@customer-portal/domain/catalog";
import { Logger } from "nestjs-pino";

@Injectable()
export class WhmcsService {
  constructor(
    private readonly connectionService: WhmcsConnectionOrchestratorService,
    private readonly invoiceService: WhmcsInvoiceService,
    private readonly subscriptionService: WhmcsSubscriptionService,
    private readonly clientService: WhmcsClientService,
    private readonly paymentService: WhmcsPaymentService,
    private readonly ssoService: WhmcsSsoService,
    private readonly orderService: WhmcsOrderService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  // ==========================================
  // INVOICE OPERATIONS (delegate to InvoiceService)
  // ==========================================

  /**
   * Get paginated invoices for a client with caching
   */
  async getInvoices(
    clientId: number,
    userId: string,
    filters: InvoiceFilters = {}
  ): Promise<InvoiceList> {
    return this.invoiceService.getInvoices(clientId, userId, filters);
  }

  /**
   * Get invoices with items (for subscription linking)
   */
  async getInvoicesWithItems(
    clientId: number,
    userId: string,
    filters: InvoiceFilters = {}
  ): Promise<InvoiceList> {
    return this.invoiceService.getInvoicesWithItems(clientId, userId, filters);
  }

  /**
   * Get individual invoice by ID with caching
   */
  async getInvoiceById(clientId: number, userId: string, invoiceId: number): Promise<Invoice> {
    return this.invoiceService.getInvoiceById(clientId, userId, invoiceId);
  }

  /**
   * Invalidate cache for a specific invoice
   */
  async invalidateInvoiceCache(userId: string, invoiceId: number): Promise<void> {
    return this.invoiceService.invalidateInvoiceCache(userId, invoiceId);
  }

  // ==========================================
  // SUBSCRIPTION OPERATIONS (delegate to SubscriptionService)
  // ==========================================

  /**
   * Get client subscriptions/services with caching
   */
  async getSubscriptions(
    clientId: number,
    userId: string,
    filters: SubscriptionFilters = {}
  ): Promise<SubscriptionList> {
    return this.subscriptionService.getSubscriptions(clientId, userId, filters);
  }

  /**
   * Get individual subscription by ID
   */
  async getSubscriptionById(
    clientId: number,
    userId: string,
    subscriptionId: number
  ): Promise<Subscription> {
    return this.subscriptionService.getSubscriptionById(clientId, userId, subscriptionId);
  }

  /**
   * Invalidate cache for a specific subscription
   */
  async invalidateSubscriptionCache(userId: string, subscriptionId: number): Promise<void> {
    return this.subscriptionService.invalidateSubscriptionCache(userId, subscriptionId);
  }

  // ==========================================
  // CLIENT OPERATIONS (delegate to ClientService)
  // ==========================================

  /**
   * Validate client login credentials
   */
  async validateLogin(
    email: string,
    password: string
  ): Promise<{ userId: number; passwordHash: string }> {
    return this.clientService.validateLogin(email, password);
  }

  /**
   * Get client details by ID
   * Returns internal WhmcsClient (type inferred)
   */
  async getClientDetails(clientId: number): Promise<WhmcsClient> {
    return this.clientService.getClientDetails(clientId);
  }

  /**
   * Get client details by email
   * Returns internal WhmcsClient (type inferred)
   */
  async getClientDetailsByEmail(email: string): Promise<WhmcsClient> {
    return this.clientService.getClientDetailsByEmail(email);
  }

  /**
   * Update client details in WHMCS
   */
  async updateClient(
    clientId: number,
    updateData: Partial<WhmcsClientResponse["client"]>
  ): Promise<void> {
    return this.clientService.updateClient(clientId, updateData);
  }

  /**
   * Convenience helpers for address get/update on WHMCS client
   */
  async getClientAddress(clientId: number): Promise<Address> {
    const customer = await this.clientService.getClientDetails(clientId);
    return addressSchema.parse(customer.address ?? {});
  }

  async updateClientAddress(clientId: number, address: Partial<Address>): Promise<void> {
    const updateData = CustomerProviders.Whmcs.prepareWhmcsClientAddressUpdate(address);
    if (Object.keys(updateData).length === 0) return;
    await this.clientService.updateClient(clientId, updateData);
  }

  /**
   * Add new client
   */
  async addClient(clientData: WhmcsAddClientParams): Promise<{ clientId: number }> {
    return this.clientService.addClient(clientData);
  }

  /**
   * Invalidate cache for a user
   */
  async invalidateUserCache(userId: string): Promise<void> {
    return this.clientService.invalidateUserCache(userId);
  }

  // ==========================================
  // PAYMENT OPERATIONS (delegate to PaymentService)
  // ==========================================

  /**
   * Get payment methods for a client
   */
  async getPaymentMethods(clientId: number, userId: string): Promise<PaymentMethodList> {
    return this.paymentService.getPaymentMethods(clientId, userId);
  }

  /**
   * Get available payment gateways
   */
  async getPaymentGateways(): Promise<PaymentGatewayList> {
    return this.paymentService.getPaymentGateways();
  }

  /**
   * Invalidate payment methods cache for a user
   */
  async invalidatePaymentMethodsCache(userId: string): Promise<void> {
    return this.paymentService.invalidatePaymentMethodsCache(userId);
  }

  /**
   * Create SSO token with payment method for invoice payment
   */
  async createPaymentSsoToken(
    clientId: number,
    invoiceId: number,
    paymentMethodId?: number,
    gatewayName?: string
  ): Promise<{ url: string; expiresAt: string }> {
    return this.paymentService.createPaymentSsoToken(
      clientId,
      invoiceId,
      paymentMethodId,
      gatewayName
    );
  }

  /**
   * Get products catalog
   */
  async getProducts(): Promise<WhmcsCatalogProductNormalized[]> {
    return this.paymentService.getProducts();
  }

  // ==========================================
  // SSO OPERATIONS (delegate to SsoService)
  // ==========================================

  /**
   * Create SSO token for WHMCS access
   */
  async createSsoToken(
    clientId: number,
    destination?: string,
    ssoRedirectPath?: string
  ): Promise<{ url: string; expiresAt: string }> {
    return this.ssoService.createSsoToken(clientId, destination, ssoRedirectPath);
  }

  /**
   * Helper function to create SSO links for invoices
   */
  async whmcsSsoForInvoice(
    clientId: number,
    invoiceId: number,
    target: "view" | "download" | "pay"
  ): Promise<string> {
    return this.ssoService.whmcsSsoForInvoice(clientId, invoiceId, target);
  }

  // ==========================================
  // CONNECTION & HEALTH (delegate to ConnectionService)
  // ==========================================

  /**
   * Health check for WHMCS API
   */
  async healthCheck(): Promise<boolean> {
    return this.connectionService.healthCheck();
  }

  /**
   * Check if WHMCS service is available
   */
  async isAvailable(): Promise<boolean> {
    return this.connectionService.isAvailable();
  }

  /**
   * Get WHMCS system information
   */
  async getSystemInfo(): Promise<unknown> {
    return this.connectionService.getSystemInfo();
  }

  async getClientsProducts(
    params: WhmcsGetClientsProductsParams
  ): Promise<WhmcsProductListResponse> {
    return this.connectionService.getClientsProducts(params);
  }

  // ==========================================
  // ORDER OPERATIONS (delegate to OrderService)
  // ==========================================

  /**
   * Get order service for direct access to order operations
   * Used by OrderProvisioningService for complex order workflows
   */
  getOrderService(): WhmcsOrderService {
    return this.orderService;
  }

  // ==========================================
  // INVOICE CREATION AND PAYMENT OPERATIONS (Used by SIM/Order services)
  // ==========================================

  /**
   * Create a new invoice for a client
   */
  async createInvoice(params: {
    clientId: number;
    description: string;
    amount: number;
    currency?: string;
    dueDate?: Date;
    notes?: string;
  }): Promise<{ id: number; number: string; total: number; status: string }> {
    return this.invoiceService.createInvoice(params);
  }

  /**
   * Update an existing invoice
   */
  async updateInvoice(params: {
    invoiceId: number;
    status?:
      | "Draft"
      | "Unpaid"
      | "Paid"
      | "Cancelled"
      | "Refunded"
      | "Collections"
      | "Payment Pending";
    dueDate?: Date;
    notes?: string;
  }): Promise<{ success: boolean; message?: string }> {
    return this.invoiceService.updateInvoice(params);
  }

  /**
   * Capture payment for an invoice
   */
  async capturePayment(params: {
    invoiceId: number;
    amount: number;
    currency?: string;
  }): Promise<{ success: boolean; transactionId?: string; error?: string }> {
    return this.invoiceService.capturePayment(params);
  }
}
