import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { FreebitFacade } from "@bff/integrations/freebit/facades/freebit.facade.js";
import { WhmcsClientService } from "@bff/integrations/whmcs/services/whmcs-client.service.js";
import { SalesforceOpportunityService } from "@bff/integrations/salesforce/services/salesforce-opportunity.service.js";
import { WorkflowCaseManager } from "@bff/modules/shared/workflow/index.js";
import { SimValidationService } from "../sim-validation.service.js";
import { SubscriptionValidationCoordinator } from "../../../shared/index.js";
import type {
  SimCancelRequest,
  SimCancelFullRequest,
  SimCancellationMonth,
  SimCancellationPreview,
} from "@customer-portal/domain/sim";
import {
  generateCancellationMonths,
  getCancellationEffectiveDate,
  getRunDateFromMonth,
} from "@customer-portal/domain/subscriptions";
import { SIM_CANCELLATION_NOTICE } from "@customer-portal/domain/opportunity";
import { SimScheduleService } from "../support/sim-schedule.service.js";
import { SimNotificationService } from "../support/sim-notification.service.js";
import { NotificationService } from "@bff/modules/notifications/notifications.service.js";
import { NOTIFICATION_SOURCE, NOTIFICATION_TYPE } from "@customer-portal/domain/notifications";

@Injectable()
export class SimCancellationService {
  // eslint-disable-next-line max-params -- NestJS DI requires individual constructor injection
  constructor(
    private readonly freebitService: FreebitFacade,
    private readonly whmcsClientService: WhmcsClientService,
    private readonly validationCoordinator: SubscriptionValidationCoordinator,
    private readonly opportunityService: SalesforceOpportunityService,
    private readonly workflowCases: WorkflowCaseManager,
    private readonly simValidation: SimValidationService,
    private readonly simSchedule: SimScheduleService,
    private readonly simNotification: SimNotificationService,
    private readonly notifications: NotificationService,
    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";
  }

  /**
   * 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 = Number.parseInt(startDateStr.slice(0, 4), 10);
    const month = Number.parseInt(startDateStr.slice(4, 6), 10) - 1;
    const day = Number.parseInt(startDateStr.slice(6, 8), 10);

    if (Number.isNaN(year) || Number.isNaN(month) || Number.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<SimCancellationPreview> {
    const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
    const simDetails = await this.freebitService.getSimDetails(validation.account);

    // Get customer info from WHMCS via coordinator
    const whmcsClientId = await this.validationCoordinator.getWhmcsClientId(userId);
    const clientDetails = await this.whmcsClientService.getClientDetails(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: generateCancellationMonths({
        includeRunDate: true,
      }) as SimCancellationMonth[],
      customerEmail,
      customerName,
    };
  }

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

    await this.simNotification.runWithNotification(
      "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, Salesforce Case + Opportunity, and email notifications)
   */
  async cancelSimFull(
    userId: string,
    subscriptionId: number,
    request: SimCancelFullRequest
  ): Promise<void> {
    this.validateCancellationConfirmations(request);

    const { runDate, cancellationDate } = this.parseCancellationDates(request.cancellationMonth);
    const context = await this.prepareCancellationContext(userId, subscriptionId);

    await this.executeFreebitCancellation({
      account: context.account,
      runDate,
      userId,
      subscriptionId,
      cancellationMonth: request.cancellationMonth,
    });

    const opportunityId = await this.createCancellationCase(
      context,
      subscriptionId,
      request,
      cancellationDate
    );

    await this.updateOpportunityIfLinked(opportunityId, cancellationDate, subscriptionId);
    await this.createCancellationNotification({
      userId,
      subscriptionId,
      cancellationMonth: request.cancellationMonth,
      runDate,
      opportunityId,
    });
    await this.sendCancellationEmails(context, request, runDate);
  }

  private validateCancellationConfirmations(request: SimCancelFullRequest): void {
    if (!request.confirmRead || !request.confirmCancel) {
      throw new BadRequestException("You must confirm both checkboxes to proceed");
    }
  }

  private parseCancellationDates(cancellationMonth: string): {
    runDate: string;
    cancellationDate: string;
  } {
    try {
      return {
        runDate: getRunDateFromMonth(cancellationMonth),
        cancellationDate: getCancellationEffectiveDate(cancellationMonth),
      };
    } catch {
      throw new BadRequestException("Invalid cancellation month format");
    }
  }

  private async prepareCancellationContext(
    userId: string,
    subscriptionId: number
  ): Promise<{
    whmcsClientId: number;
    sfAccountId: string;
    account: string;
    iccid: string | undefined;
    customerName: string;
    customerEmail: string;
  }> {
    const { whmcsClientId, sfAccountId } =
      await this.validationCoordinator.validateAccountMapping(userId);
    const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
    const simDetails = await this.freebitService.getSimDetails(validation.account);
    const clientDetails = await this.whmcsClientService.getClientDetails(whmcsClientId);

    return {
      whmcsClientId,
      sfAccountId,
      account: validation.account,
      iccid: simDetails.iccid,
      customerName:
        `${clientDetails.firstname || ""} ${clientDetails.lastname || ""}`.trim() || "Customer",
      customerEmail: clientDetails.email || "",
    };
  }

  private async executeFreebitCancellation(params: {
    account: string;
    runDate: string;
    userId: string;
    subscriptionId: number;
    cancellationMonth: string;
  }): Promise<void> {
    const { account, runDate, userId, subscriptionId, cancellationMonth } = params;
    this.logger.log(`Processing SIM cancellation via PA02-04`, {
      userId,
      subscriptionId,
      account,
      cancellationMonth,
      runDate,
    });

    await this.freebitService.cancelAccount(account, runDate);

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

  private async createCancellationCase(
    context: { sfAccountId: string; account: string; iccid: string | undefined },
    subscriptionId: number,
    request: SimCancelFullRequest,
    cancellationDate: string
  ): Promise<string | null> {
    const opportunityResolution = await this.validationCoordinator.resolveOpportunityId(
      subscriptionId,
      { fallbackToSalesforce: true }
    );
    const opportunityId = opportunityResolution.opportunityId;

    await this.workflowCases.notifySimCancellation({
      accountId: context.sfAccountId,
      ...(opportunityId ? { opportunityId } : {}),
      simAccount: context.account,
      iccid: context.iccid || "N/A",
      subscriptionId,
      cancellationMonth: request.cancellationMonth,
      serviceEndDate: cancellationDate,
      ...(request.comments ? { comments: request.comments } : {}),
    });

    this.logger.log("SIM cancellation case created via WorkflowCaseManager", {
      sfAccountIdTail: context.sfAccountId.slice(-4),
      opportunityId: opportunityId ? opportunityId.slice(-4) : null,
    });

    return opportunityId;
  }

  private async updateOpportunityIfLinked(
    opportunityId: string | null,
    cancellationDate: string,
    subscriptionId: number
  ): Promise<void> {
    if (!opportunityId) {
      this.logger.debug("No Opportunity linked to SIM subscription", { subscriptionId });
      return;
    }

    try {
      await this.opportunityService.updateSimCancellationData(opportunityId, {
        scheduledCancellationDate: `${cancellationDate}T23:59:59.000Z`,
        cancellationNotice: SIM_CANCELLATION_NOTICE.RECEIVED,
      });
      this.logger.log("Opportunity updated with SIM cancellation data", {
        opportunityId,
        scheduledDate: cancellationDate,
      });
    } catch (error) {
      this.logger.warn("Failed to update Opportunity with SIM cancellation data", {
        error: error instanceof Error ? error.message : String(error),
        subscriptionId,
        opportunityId,
      });
    }
  }

  private async createCancellationNotification(params: {
    userId: string;
    subscriptionId: number;
    cancellationMonth: string;
    runDate: string;
    opportunityId: string | null;
  }): Promise<void> {
    const { userId, subscriptionId, cancellationMonth, runDate, opportunityId } = params;
    const caseId = `cancellation:sim:${subscriptionId}:${cancellationMonth}`;
    try {
      await this.notifications.createNotification({
        userId,
        type: NOTIFICATION_TYPE.CANCELLATION_SCHEDULED,
        source: NOTIFICATION_SOURCE.SYSTEM,
        sourceId: caseId || opportunityId || `sim:${subscriptionId}:${runDate}`,
        actionUrl: `/account/services/${subscriptionId}`,
      });
    } catch (error) {
      this.logger.warn("Failed to create SIM cancellation notification", {
        userId,
        subscriptionId,
        runDate,
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  private async sendCancellationEmails(
    context: {
      account: string;
      iccid: string | undefined;
      customerName: string;
      customerEmail: string;
    },
    request: SimCancelFullRequest,
    runDate: string
  ): Promise<void> {
    const adminEmailBody = this.simNotification.buildCancellationAdminEmail({
      customerName: context.customerName,
      simNumber: context.account,
      ...(context.iccid === undefined ? {} : { serialNumber: context.iccid }),
      cancellationMonth: request.cancellationMonth,
      registeredEmail: context.customerEmail,
      ...(request.comments === undefined ? {} : { comments: request.comments }),
    });

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

    await this.simNotification.sendCustomerEmail(
      context.customerEmail,
      "SonixNet SIM Cancellation Confirmation",
      this.buildCustomerConfirmationEmail(
        context.customerName,
        context.account,
        request.cancellationMonth
      )
    );
  }

  private buildCustomerConfirmationEmail(
    customerName: string,
    account: string,
    cancellationMonth: string
  ): string {
    return `Dear ${customerName},

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

The cancellation will take effect at the end of ${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`;
  }
}
