/**
 * Internet Cancellation Service
 *
 * Handles Internet service cancellation flows:
 * - Preview available cancellation months
 * - Submit cancellation requests (creates SF Case + updates Opportunity)
 *
 * Internet cancellation differs from SIM in that:
 * - No Freebit/MVNO API calls needed
 * - Cancellation is processed via Salesforce Case workflow
 * - Equipment return may be required (ONU, router)
 */

import { Injectable, Inject, BadRequestException, NotFoundException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { WhmcsConnectionFacade } from "@bff/integrations/whmcs/facades/whmcs.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 { EmailService } from "@bff/infra/email/email.service.js";
import { NotificationService } from "@bff/modules/notifications/notifications.service.js";
import { SubscriptionValidationCoordinator } from "../../shared/index.js";
import {
  generateCancellationMonths,
  getCancellationEffectiveDate,
  type InternetCancellationPreview,
  type InternetCancellationMonth,
  type InternetCancelRequest,
} from "@customer-portal/domain/subscriptions";
import {
  type InternetCancellationOpportunityData,
  CANCELLATION_NOTICE,
  LINE_RETURN_STATUS,
} from "@customer-portal/domain/opportunity";
import { NOTIFICATION_SOURCE, NOTIFICATION_TYPE } from "@customer-portal/domain/notifications";

@Injectable()
export class InternetCancellationService {
  // eslint-disable-next-line max-params -- NestJS DI requires individual constructor injection
  constructor(
    private readonly whmcsConnectionService: WhmcsConnectionFacade,
    private readonly whmcsClientService: WhmcsClientService,
    private readonly validationCoordinator: SubscriptionValidationCoordinator,
    private readonly opportunityService: SalesforceOpportunityService,
    private readonly workflowCases: WorkflowCaseManager,
    private readonly emailService: EmailService,
    private readonly notifications: NotificationService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Validate that the subscription belongs to the user and is an Internet service.
   * Uses SubscriptionValidationCoordinator for account mapping and product type detection.
   */
  private async validateInternetSubscription(
    userId: string,
    subscriptionId: number
  ): Promise<{
    whmcsClientId: number;
    sfAccountId: string;
    subscription: {
      id: number;
      productName: string;
      amount: number;
      nextDue?: string;
      registrationDate?: string;
    };
  }> {
    // Validate account mapping via coordinator
    const { whmcsClientId, sfAccountId } =
      await this.validationCoordinator.validateAccountMapping(userId);

    // Get subscription from WHMCS
    const productsResponse = await this.whmcsConnectionService.getClientsProducts({
      clientid: whmcsClientId,
    });
    const productContainer = productsResponse.products?.product;
    const products = Array.isArray(productContainer)
      ? productContainer
      : productContainer
        ? [productContainer]
        : [];

    const subscription = products.find(
      (p: { id?: number | string }) => Number(p.id) === subscriptionId
    );

    if (!subscription) {
      throw new NotFoundException("Subscription not found");
    }

    // Verify it's an Internet service via coordinator
    const productName = String(subscription.name || subscription.groupname || "");
    if (!this.validationCoordinator.isInternetService(productName)) {
      throw new BadRequestException("This endpoint is only for Internet subscriptions");
    }

    return {
      whmcsClientId,
      sfAccountId,
      subscription: {
        id: Number(subscription.id),
        productName: productName,
        amount: Number.parseFloat(String(subscription.amount || subscription.recurringamount || 0)),
        nextDue: String(subscription.nextduedate || ""),
        registrationDate: String(subscription.regdate || ""),
      },
    };
  }

  /**
   * Get cancellation preview with available months and service details
   */
  async getCancellationPreview(
    userId: string,
    subscriptionId: number
  ): Promise<InternetCancellationPreview> {
    const { whmcsClientId, subscription } = await this.validateInternetSubscription(
      userId,
      subscriptionId
    );

    // Get customer info from WHMCS
    const clientDetails = await this.whmcsClientService.getClientDetails(whmcsClientId);
    const customerName =
      `${clientDetails.firstname || ""} ${clientDetails.lastname || ""}`.trim() || "Customer";
    const customerEmail = clientDetails.email || "";

    return {
      productName: subscription.productName,
      billingAmount: subscription.amount,
      nextDueDate: subscription.nextDue,
      registrationDate: subscription.registrationDate,
      availableMonths: generateCancellationMonths() as InternetCancellationMonth[],
      customerEmail,
      customerName,
    };
  }

  /**
   * Submit Internet cancellation request. Creates a Salesforce Case and updates the Opportunity.
   */
  async submitCancellation(
    userId: string,
    subscriptionId: number,
    request: InternetCancelRequest
  ): Promise<void> {
    this.validateCancellationConfirmations(request);
    const cancellationDate = this.parseCancellationDate(request.cancellationMonth);

    const { whmcsClientId, sfAccountId, subscription } = await this.validateInternetSubscription(
      userId,
      subscriptionId
    );

    this.logger.log("Processing Internet cancellation request", {
      userId,
      subscriptionId,
      cancellationMonth: request.cancellationMonth,
      cancellationDate,
    });

    const { customerName, customerEmail } = await this.getCustomerInfo(whmcsClientId);
    const opportunityId = await this.createCancellationCase(
      sfAccountId,
      subscriptionId,
      request,
      cancellationDate
    );

    await this.createCancellationNotification(userId, subscriptionId, request);
    await this.updateOpportunityIfLinked(opportunityId, cancellationDate);
    await this.sendConfirmationEmail(
      customerName,
      customerEmail,
      subscription.productName,
      request
    );

    this.logger.log("Internet cancellation request processed successfully", {
      userId,
      subscriptionId,
      caseId: `cancellation:internet:${subscriptionId}:${request.cancellationMonth}`,
      cancellationMonth: request.cancellationMonth,
    });
  }

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

  private parseCancellationDate(cancellationMonth: string): string {
    try {
      return getCancellationEffectiveDate(cancellationMonth);
    } catch {
      throw new BadRequestException("Invalid cancellation month format");
    }
  }

  private async getCustomerInfo(whmcsClientId: number): Promise<{
    customerName: string;
    customerEmail: string;
  }> {
    const clientDetails = await this.whmcsClientService.getClientDetails(whmcsClientId);
    return {
      customerName:
        `${clientDetails.firstname || ""} ${clientDetails.lastname || ""}`.trim() || "Customer",
      customerEmail: clientDetails.email || "",
    };
  }

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

    await this.workflowCases.notifyInternetCancellation({
      accountId: sfAccountId,
      ...(opportunityId ? { opportunityId } : {}),
      subscriptionId,
      cancellationMonth: request.cancellationMonth,
      serviceEndDate: cancellationDate,
      ...(request.comments ? { comments: request.comments } : {}),
    });

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

    return opportunityId;
  }

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

  private async updateOpportunityIfLinked(
    opportunityId: string | null,
    cancellationDate: string
  ): Promise<void> {
    if (!opportunityId) return;

    try {
      const cancellationData: InternetCancellationOpportunityData = {
        scheduledCancellationDate: `${cancellationDate}T23:59:59.000Z`,
        cancellationNotice: CANCELLATION_NOTICE.RECEIVED,
        lineReturnStatus: LINE_RETURN_STATUS.NOT_YET,
      };
      await this.opportunityService.updateInternetCancellationData(opportunityId, cancellationData);
      this.logger.log("Opportunity updated with cancellation data", {
        opportunityId,
        scheduledDate: cancellationDate,
      });
    } catch (error) {
      this.logger.error("Failed to update Opportunity cancellation data", {
        error: error instanceof Error ? error.message : "Unknown error",
        opportunityId,
      });
    }
  }

  private async sendConfirmationEmail(
    customerName: string,
    customerEmail: string,
    productName: string,
    request: InternetCancelRequest
  ): Promise<void> {
    const confirmationBody = `Dear ${customerName},

Your cancellation request for your Internet service has been received.

Service: ${productName}
Cancellation effective: End of ${request.cancellationMonth}

Our team will contact you regarding equipment return (ONU/router) if applicable.

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`;

    try {
      await this.emailService.sendEmail({
        to: customerEmail,
        subject: "SonixNet Internet Cancellation Confirmation",
        text: confirmationBody,
      });
    } catch (error) {
      this.logger.error("Failed to send cancellation confirmation email", {
        error: error instanceof Error ? error.message : "Unknown error",
        customerEmail,
      });
    }
  }
}
