import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { FreebitOrchestratorService } from "@bff/integrations/freebit/services/freebit-orchestrator.service";
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { SimValidationService } from "./sim-validation.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type { SimCancelRequest, SimCancelFullRequest } from "@customer-portal/domain/sim";
import { SimScheduleService } from "./sim-schedule.service";
import { SimActionRunnerService } from "./sim-action-runner.service";
import { SimApiNotificationService } from "./sim-api-notification.service";

export interface CancellationMonth {
  value: string; // YYYY-MM format
  label: string; // Display label like "November 2025"
  runDate: string; // YYYYMMDD format for API (1st of next month)
}

export interface CancellationPreview {
  simNumber: string;
  serialNumber?: string;
  planCode: string;
  startDate?: string;
  minimumContractEndDate?: string;
  isWithinMinimumTerm: boolean;
  availableMonths: CancellationMonth[];
  customerEmail: string;
  customerName: string;
}

@Injectable()
export class SimCancellationService {
  constructor(
    private readonly freebitService: FreebitOrchestratorService,
    private readonly whmcsService: WhmcsService,
    private readonly mappingsService: MappingsService,
    private readonly simValidation: SimValidationService,
    private readonly simSchedule: SimScheduleService,
    private readonly simActionRunner: SimActionRunnerService,
    private readonly apiNotification: SimApiNotificationService,
    private readonly configService: ConfigService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  private get freebitBaseUrl(): string {
    return this.configService.get<string>("FREEBIT_BASE_URL") || "https://i1.mvno.net/emptool/api";
  }

  /**
   * Generate available cancellation months (next 12 months)
   */
  private generateCancellationMonths(): CancellationMonth[] {
    const months: CancellationMonth[] = [];
    const today = new Date();
    const dayOfMonth = today.getDate();

    // Start from current month if before 25th, otherwise next month
    const startOffset = dayOfMonth <= 25 ? 0 : 1;

    for (let i = startOffset; i < startOffset + 12; i++) {
      const date = new Date(today.getFullYear(), today.getMonth() + i, 1);
      const year = date.getFullYear();
      const month = date.getMonth() + 1;
      const monthStr = String(month).padStart(2, "0");

      // runDate is the 1st of the NEXT month (cancellation takes effect at month end)
      const nextMonth = new Date(year, month, 1);
      const runYear = nextMonth.getFullYear();
      const runMonth = String(nextMonth.getMonth() + 1).padStart(2, "0");

      months.push({
        value: `${year}-${monthStr}`,
        label: date.toLocaleDateString("en-US", { month: "long", year: "numeric" }),
        runDate: `${runYear}${runMonth}01`,
      });
    }

    return months;
  }

  /**
   * Calculate minimum contract end date (3 months after start, signup month not included)
   */
  private calculateMinimumContractEndDate(startDateStr: string): Date | null {
    if (!startDateStr || startDateStr.length < 8) return null;

    // Parse YYYYMMDD format
    const year = parseInt(startDateStr.substring(0, 4), 10);
    const month = parseInt(startDateStr.substring(4, 6), 10) - 1;
    const day = parseInt(startDateStr.substring(6, 8), 10);

    if (isNaN(year) || isNaN(month) || isNaN(day)) return null;

    const startDate = new Date(year, month, day);
    // Minimum term is 3 months after signup month (signup month not included)
    // e.g., signup in January = minimum term ends April 30
    const endDate = new Date(startDate.getFullYear(), startDate.getMonth() + 4, 0);

    return endDate;
  }

  /**
   * Get cancellation preview with available months
   */
  async getCancellationPreview(
    userId: string,
    subscriptionId: number
  ): Promise<CancellationPreview> {
    const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
    const simDetails = await this.freebitService.getSimDetails(validation.account);

    // Get customer info from WHMCS
    const mapping = await this.mappingsService.findByUserId(userId);
    if (!mapping?.whmcsClientId) {
      throw new BadRequestException("WHMCS client mapping not found");
    }

    const clientDetails = await this.whmcsService.getClientDetails(mapping.whmcsClientId);
    const customerName = `${clientDetails.firstname || ""} ${clientDetails.lastname || ""}`.trim() || "Customer";
    const customerEmail = clientDetails.email || "";

    // Calculate minimum contract end date
    const startDate = simDetails.startDate;
    const minEndDate = startDate ? this.calculateMinimumContractEndDate(startDate) : null;
    const today = new Date();
    const isWithinMinimumTerm = minEndDate ? today < minEndDate : false;

    // Format minimum contract end date for display
    let minimumContractEndDate: string | undefined;
    if (minEndDate) {
      const year = minEndDate.getFullYear();
      const month = String(minEndDate.getMonth() + 1).padStart(2, "0");
      minimumContractEndDate = `${year}-${month}`;
    }

    return {
      simNumber: validation.account,
      serialNumber: simDetails.iccid,
      planCode: simDetails.planCode,
      startDate,
      minimumContractEndDate,
      isWithinMinimumTerm,
      availableMonths: this.generateCancellationMonths(),
      customerEmail,
      customerName,
    };
  }

  /**
   * Cancel SIM service (legacy)
   */
  async cancelSim(
    userId: string,
    subscriptionId: number,
    request: SimCancelRequest = {}
  ): Promise<void> {
    let account = "";

    await this.simActionRunner.run(
      "Cancel SIM",
      {
        baseContext: {
          userId,
          subscriptionId,
          scheduledAt: request.scheduledAt,
        },
        enrichSuccess: result => ({
          account: result.account,
          runDate: result.runDate,
        }),
        enrichError: () => ({
          account,
        }),
      },
      async () => {
        const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
        account = validation.account;

        const scheduleResolution = this.simSchedule.resolveScheduledDate(request.scheduledAt);

        await this.freebitService.cancelSim(account, scheduleResolution.date);

        this.logger.log(`Successfully cancelled SIM for subscription ${subscriptionId}`, {
          userId,
          subscriptionId,
          account,
          runDate: scheduleResolution.date,
        });

        return {
          account,
          runDate: scheduleResolution.date,
        };
      }
    );
  }

  /**
   * Cancel SIM service with full flow (PA02-04 and email notifications)
   */
  async cancelSimFull(
    userId: string,
    subscriptionId: number,
    request: SimCancelFullRequest
  ): Promise<void> {
    const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
    const account = validation.account;
    const simDetails = await this.freebitService.getSimDetails(account);

    // Get customer info from WHMCS
    const mapping = await this.mappingsService.findByUserId(userId);
    if (!mapping?.whmcsClientId) {
      throw new BadRequestException("WHMCS client mapping not found");
    }

    const clientDetails = await this.whmcsService.getClientDetails(mapping.whmcsClientId);
    const customerName = `${clientDetails.firstname || ""} ${clientDetails.lastname || ""}`.trim() || "Customer";
    const customerEmail = clientDetails.email || "";

    // Validate confirmations
    if (!request.confirmRead || !request.confirmCancel) {
      throw new BadRequestException("You must confirm both checkboxes to proceed");
    }

    // Parse cancellation month and calculate runDate
    const [year, month] = request.cancellationMonth.split("-").map(Number);
    if (!year || !month) {
      throw new BadRequestException("Invalid cancellation month format");
    }

    // runDate is 1st of the NEXT month (cancellation at end of selected month)
    const nextMonth = new Date(year, month, 1);
    const runYear = nextMonth.getFullYear();
    const runMonth = String(nextMonth.getMonth() + 1).padStart(2, "0");
    const runDate = `${runYear}${runMonth}01`;

    this.logger.log(`Processing SIM cancellation via PA02-04`, {
      userId,
      subscriptionId,
      account,
      cancellationMonth: request.cancellationMonth,
      runDate,
    });

    // Call PA02-04 cancellation API
    await this.freebitService.cancelAccount(account, runDate);

    this.logger.log(`Successfully cancelled SIM for subscription ${subscriptionId}`, {
      userId,
      subscriptionId,
      account,
      runDate,
    });

    // Send admin notification email
    const adminEmailBody = this.apiNotification.buildCancellationAdminEmail({
      customerName,
      simNumber: account,
      serialNumber: simDetails.iccid,
      cancellationMonth: request.cancellationMonth,
      registeredEmail: customerEmail,
      otherEmail: request.alternativeEmail || undefined,
      comments: request.comments,
    });

    await this.apiNotification.sendApiResultsEmail(
      "SonixNet SIM Online Cancellation",
      [
        {
          url: `${this.freebitBaseUrl}/master/cnclAcnt/`,
          json: {
            kind: "MVNO",
            account,
            runDate,
            authKey: "[REDACTED]",
          },
          result: {
            resultCode: "100",
            status: { message: "OK", statusCode: "200" },
          },
        },
      ],
      adminEmailBody
    );

    // Send confirmation email to customer (and alternative if provided)
    const confirmationSubject = "SonixNet SIM Cancellation Confirmation";
    const confirmationBody = `Dear ${customerName},

Your cancellation request for SIM #${account} has been confirmed.

The cancellation will take effect at the end of ${request.cancellationMonth}.

If you have any questions, please contact us at info@asolutions.co.jp

With best regards,
Assist Solutions Customer Support
TEL: 0120-660-470 (Mon-Fri / 10AM-6PM)
Email: info@asolutions.co.jp`;

    await this.apiNotification.sendCustomerEmail(
      customerEmail,
      confirmationSubject,
      confirmationBody
    );

    // Send to alternative email if provided
    if (request.alternativeEmail && request.alternativeEmail !== customerEmail) {
      await this.apiNotification.sendCustomerEmail(
        request.alternativeEmail,
        confirmationSubject,
        confirmationBody
      );
    }
  }
}
