import { Injectable, Logger } from "@nestjs/common";
import type {
  CancellationPreview,
  CancellationStatus,
  InternetCancelRequest,
} from "@customer-portal/domain/subscriptions";
import type { SimCancelFullRequest } from "@customer-portal/domain/sim";
import { OPPORTUNITY_STAGE } from "@customer-portal/domain/opportunity";
import { SubscriptionsOrchestrator } from "../subscriptions-orchestrator.service.js";
import { InternetCancellationService } from "../internet-management/services/internet-cancellation.service.js";
import { SimCancellationService } from "../sim-management/services/mutations/sim-cancellation.service.js";
import { SalesforceOpportunityService } from "@bff/integrations/salesforce/services/salesforce-opportunity.service.js";
import { CANCELLATION_STEP3_NOTICES, CANCELLATION_TERMS } from "./cancellation-terms.config.js";
import { SubscriptionValidationCoordinator } from "../shared/index.js";

// Valid stages for portal display
const VALID_PORTAL_STAGES: ReadonlySet<string> = new Set([
  OPPORTUNITY_STAGE.ACTIVE,
  OPPORTUNITY_STAGE.CANCELLING,
  OPPORTUNITY_STAGE.CANCELLED,
]);

type PortalStage = "Active" | "△Cancelling" | "〇Cancelled";

function isValidPortalStage(stage: string): stage is PortalStage {
  return VALID_PORTAL_STAGES.has(stage);
}

@Injectable()
export class CancellationService {
  private readonly logger = new Logger(CancellationService.name);

  // eslint-disable-next-line max-params -- NestJS dependency injection requires all services to be injected via constructor
  constructor(
    private readonly subscriptionsOrchestrator: SubscriptionsOrchestrator,
    private readonly opportunityService: SalesforceOpportunityService,
    private readonly internetCancellation: InternetCancellationService,
    private readonly simCancellation: SimCancellationService,
    private readonly validationCoordinator: SubscriptionValidationCoordinator
  ) {}

  /**
   * Get unified cancellation preview for any service type.
   *
   * This method:
   * 1. Fetches the subscription from WHMCS (includes custom fields)
   * 2. Detects service type from product name
   * 3. Queries Opportunity status by ID (fast, no SOQL) only when WHMCS is still Active
   * 4. Delegates to service-specific preview logic
   */
  async getPreview(userId: string, subscriptionId: number): Promise<CancellationPreview> {
    // 1) Read subscription from WHMCS (includes custom fields)
    const subscription = await this.subscriptionsOrchestrator.getSubscriptionById(
      userId,
      subscriptionId
    );

    // Use coordinator for service type detection and opportunityId resolution
    const serviceType = this.validationCoordinator.detectServiceType(subscription.productName);
    const opportunityResolution = await this.validationCoordinator.resolveOpportunityId(
      subscriptionId,
      { customFields: subscription.customFields, fallbackToSalesforce: false }
    );
    const opportunityId = opportunityResolution.opportunityId ?? undefined;

    // 2) Query Opportunity status ONLY when WHMCS is Active (not already cancelled)
    const shouldQueryOpp = subscription.status === "Active" && Boolean(opportunityId);

    // 3) Service-specific handling
    if (serviceType === "internet") {
      return this.buildInternetPreview(userId, subscriptionId, opportunityId, shouldQueryOpp);
    }

    return this.buildSimPreview(userId, subscriptionId, opportunityId, shouldQueryOpp);
  }

  /**
   * Submit cancellation request (routes to appropriate handler)
   */
  async submit(
    userId: string,
    subscriptionId: number,
    request: InternetCancelRequest
  ): Promise<void> {
    const subscription = await this.subscriptionsOrchestrator.getSubscriptionById(
      userId,
      subscriptionId
    );
    const serviceType = this.validationCoordinator.detectServiceType(subscription.productName);

    if (serviceType === "internet") {
      await this.internetCancellation.submitCancellation(userId, subscriptionId, request);
      return;
    }

    // SIM full flow expects the same shape (structural typing is compatible)
    await this.simCancellation.cancelSimFull(
      userId,
      subscriptionId,
      request as SimCancelFullRequest
    );
  }

  // =========================================================================
  // Private Helpers
  // =========================================================================

  private async buildInternetPreview(
    userId: string,
    subscriptionId: number,
    opportunityId: string | undefined,
    shouldQueryOpp: boolean
  ): Promise<CancellationPreview> {
    const preview = await this.internetCancellation.getCancellationPreview(userId, subscriptionId);

    // Query Opportunity status by ID (fast direct lookup)
    const cancellationStatus =
      shouldQueryOpp && opportunityId
        ? await this.getInternetCancellationStatus(opportunityId)
        : null;

    return {
      serviceType: "internet",
      serviceName: preview.productName,
      opportunityId,
      serviceInfo: [
        { label: "Service", value: preview.productName },
        { label: "Monthly", value: `¥${preview.billingAmount.toLocaleString()}` },
        { label: "Next Due", value: preview.nextDueDate || "—" },
      ],
      terms: CANCELLATION_TERMS.internet,
      warnings: [],
      step3Notices: CANCELLATION_STEP3_NOTICES.internet,
      cancellationStatus,
      availableMonths: preview.availableMonths,
      customerEmail: preview.customerEmail,
      customerName: preview.customerName,
    };
  }

  private async buildSimPreview(
    userId: string,
    subscriptionId: number,
    opportunityId: string | undefined,
    shouldQueryOpp: boolean
  ): Promise<CancellationPreview> {
    const preview = await this.simCancellation.getCancellationPreview(userId, subscriptionId);

    // Query Opportunity status by ID (fast direct lookup)
    const cancellationStatus =
      shouldQueryOpp && opportunityId ? await this.getSimCancellationStatus(opportunityId) : null;

    const warnings =
      preview.isWithinMinimumTerm && preview.minimumContractEndDate
        ? [
            {
              title: "Minimum Contract Term Warning",
              content: `Your subscription is within the minimum contract period (ends ${preview.minimumContractEndDate}). Early cancellation may incur additional charges.`,
            },
          ]
        : [];

    return {
      serviceType: "sim",
      serviceName: `SIM: ${preview.simNumber}`,
      opportunityId,
      serviceInfo: [
        { label: "SIM Number", value: preview.simNumber },
        { label: "Serial #", value: preview.serialNumber || "—", mono: true },
        { label: "Start Date", value: preview.startDate || "—" },
      ],
      terms: CANCELLATION_TERMS.sim,
      warnings,
      step3Notices: CANCELLATION_STEP3_NOTICES.sim,
      cancellationStatus,
      availableMonths: preview.availableMonths,
      customerEmail: preview.customerEmail,
      customerName: preview.customerName,
      isWithinMinimumTerm: preview.isWithinMinimumTerm,
      minimumContractEndDate: preview.minimumContractEndDate,
    };
  }

  private async getInternetCancellationStatus(opportunityId: string): Promise<CancellationStatus> {
    const oppStatus =
      await this.opportunityService.getInternetCancellationStatusByOpportunityId(opportunityId);
    if (!oppStatus) return null;

    // Only return valid portal stages
    if (!isValidPortalStage(oppStatus.stage)) {
      this.logger.debug(
        `Opportunity ${opportunityId} stage "${oppStatus.stage}" not shown in portal`
      );
      return null;
    }

    return {
      stage: oppStatus.stage,
      scheduledEndDate: oppStatus.scheduledEndDate,
      rentalReturnStatus: oppStatus.rentalReturnStatus,
    };
  }

  private async getSimCancellationStatus(opportunityId: string): Promise<CancellationStatus> {
    const oppStatus =
      await this.opportunityService.getSimCancellationStatusByOpportunityId(opportunityId);
    if (!oppStatus) return null;

    // Only return valid portal stages
    if (!isValidPortalStage(oppStatus.stage)) {
      this.logger.debug(
        `Opportunity ${opportunityId} stage "${oppStatus.stage}" not shown in portal`
      );
      return null;
    }

    return {
      stage: oppStatus.stage,
      scheduledEndDate: oppStatus.scheduledEndDate,
    };
  }
}
