/**
 * Workflow Case Manager
 *
 * Centralizes creation of internal workflow cases for CS team visibility.
 * All cases created by this service use PORTAL_NOTIFICATION origin,
 * meaning they are visible only to internal CS staff, not customers.
 *
 * Provides typed methods for each workflow case type with consistent:
 * - Salesforce Lightning URL building
 * - Description formatting
 * - Error handling (critical vs non-critical)
 * - Logging
 */

import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { SalesforceCaseService } from "@bff/integrations/salesforce/services/salesforce-case.service.js";
import { SalesforceConnection } from "@bff/integrations/salesforce/services/salesforce-connection.service.js";
import { SALESFORCE_CASE_ORIGIN } from "@customer-portal/domain/support/providers";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import type {
  OrderPlacedCaseParams,
  EligibilityCheckCaseParams,
  InternetCancellationCaseParams,
  SimCancellationCaseParams,
  IdVerificationCaseParams,
} from "./workflow-case-manager.types.js";

@Injectable()
export class WorkflowCaseManager {
  constructor(
    private readonly caseService: SalesforceCaseService,
    private readonly sfConnection: SalesforceConnection,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  // ============================================================================
  // Public Methods - One per workflow case type
  // ============================================================================

  /**
   * Create a notification case when an order is placed.
   * Non-critical: logs warning on failure, does not throw.
   */
  async notifyOrderPlaced(params: OrderPlacedCaseParams): Promise<void> {
    const { accountId, orderId, orderType, opportunityId, opportunityCreated } = params;

    try {
      const orderLink = this.buildLightningUrl("Order", orderId);
      const opportunityLink = opportunityId
        ? this.buildLightningUrl("Opportunity", opportunityId)
        : null;

      const opportunityStatus = opportunityId
        ? opportunityCreated
          ? "Created new opportunity for this order"
          : "Linked to existing opportunity"
        : "No opportunity linked";

      const description = this.buildDescription([
        "Order placed via Customer Portal.",
        "",
        `Order ID: ${orderId}`,
        orderLink ? `Order: ${orderLink}` : null,
        "",
        opportunityId ? `Opportunity ID: ${opportunityId}` : null,
        opportunityLink ? `Opportunity: ${opportunityLink}` : null,
        `Opportunity Status: ${opportunityStatus}`,
      ]);

      await this.caseService.createCase({
        accountId,
        opportunityId,
        subject: `Order Placed - ${orderType} (Portal)`,
        description,
        origin: SALESFORCE_CASE_ORIGIN.PORTAL_NOTIFICATION,
      });

      this.logger.log("Created order placed case", {
        orderIdTail: orderId.slice(-4),
        opportunityIdTail: opportunityId?.slice(-4),
        opportunityCreated,
      });
    } catch (error) {
      this.logger.warn("Failed to create order placed case", {
        orderIdTail: orderId.slice(-4),
        error: extractErrorMessage(error),
      });
    }
  }

  /**
   * Create a case for internet eligibility check request.
   * Non-critical: logs warning on failure, does not throw.
   *
   * Stores an address key (postcode:streetAddress) for Salesforce Flow
   * to use for duplicate detection.
   */
  async notifyEligibilityCheck(params: EligibilityCheckCaseParams): Promise<void> {
    const { accountId, address, streetAddress, opportunityId, opportunityCreated } = params;

    try {
      const opportunityLink = opportunityId
        ? this.buildLightningUrl("Opportunity", opportunityId)
        : null;

      const opportunityStatus = opportunityCreated
        ? "Created new opportunity for this request"
        : "Linked to existing opportunity";

      const formattedAddress = this.formatAddress(address);

      // Create address key for Salesforce Flow duplicate detection
      const addressKey = this.createAddressKey(address.postcode, streetAddress);

      const description = this.buildDescription([
        "Customer requested to check if internet service is available at the following address:",
        "",
        formattedAddress,
        "",
        opportunityLink ? `Opportunity: ${opportunityLink}` : null,
        `Opportunity Status: ${opportunityStatus}`,
      ]);

      await this.caseService.createCase({
        accountId,
        opportunityId,
        subject: "Internet availability check request (Portal)",
        description,
        origin: SALESFORCE_CASE_ORIGIN.PORTAL_NOTIFICATION,
        // Store address key for Salesforce Flow duplicate detection
        eligibilityAddressKey: addressKey ?? undefined,
      });

      this.logger.log("Created eligibility check case", {
        accountIdTail: accountId.slice(-4),
        opportunityIdTail: opportunityId?.slice(-4),
        opportunityCreated,
        hasAddressKey: !!addressKey,
      });
    } catch (error) {
      this.logger.warn("Failed to create eligibility check case", {
        accountIdTail: accountId.slice(-4),
        error: extractErrorMessage(error),
      });
    }
  }

  /**
   * Create a case for internet service cancellation request.
   * Non-critical: logs warning on failure, does not throw.
   */
  async notifyInternetCancellation(params: InternetCancellationCaseParams): Promise<void> {
    const {
      accountId,
      subscriptionId,
      cancellationMonth,
      serviceEndDate,
      comments,
      opportunityId,
    } = params;

    try {
      const description = this.buildDescription([
        "Cancellation Request from Portal",
        "",
        "Product Type: Internet",
        `WHMCS Service ID: ${subscriptionId}`,
        `Cancellation Month: ${cancellationMonth}`,
        `Service End Date: ${serviceEndDate}`,
        "",
        comments ? "Customer Comments:" : null,
        comments || null,
        "",
        `Submitted: ${new Date().toISOString()}`,
      ]);

      await this.caseService.createCase({
        accountId,
        opportunityId,
        subject: `Cancellation Request - Internet (${cancellationMonth})`,
        description,
        origin: SALESFORCE_CASE_ORIGIN.PORTAL_NOTIFICATION,
        priority: "High",
      });

      this.logger.log("Created internet cancellation case", {
        accountIdTail: accountId.slice(-4),
        subscriptionId,
        cancellationMonth,
      });
    } catch (error) {
      this.logger.warn("Failed to create internet cancellation case", {
        accountIdTail: accountId.slice(-4),
        subscriptionId,
        error: extractErrorMessage(error),
      });
    }
  }

  /**
   * Create a case for SIM service cancellation request.
   * Non-critical: logs warning on failure, does not throw.
   */
  async notifySimCancellation(params: SimCancellationCaseParams): Promise<void> {
    const {
      accountId,
      simAccount,
      iccid,
      subscriptionId,
      cancellationMonth,
      serviceEndDate,
      comments,
      opportunityId,
    } = params;

    try {
      const description = this.buildDescription([
        "Cancellation Request from Portal",
        "",
        "Product Type: SIM",
        `SIM Number: ${simAccount}`,
        `Serial Number: ${iccid || "N/A"}`,
        `WHMCS Service ID: ${subscriptionId}`,
        `Cancellation Month: ${cancellationMonth}`,
        `Service End Date: ${serviceEndDate}`,
        "",
        comments ? "Customer Comments:" : null,
        comments || null,
        "",
        `Submitted: ${new Date().toISOString()}`,
      ]);

      await this.caseService.createCase({
        accountId,
        opportunityId,
        subject: `Cancellation Request - SIM (${cancellationMonth})`,
        description,
        origin: SALESFORCE_CASE_ORIGIN.PORTAL_NOTIFICATION,
        priority: "High",
      });

      this.logger.log("Created SIM cancellation case", {
        accountIdTail: accountId.slice(-4),
        simAccount,
        subscriptionId,
        cancellationMonth,
      });
    } catch (error) {
      this.logger.warn("Failed to create SIM cancellation case", {
        accountIdTail: accountId.slice(-4),
        subscriptionId,
        error: extractErrorMessage(error),
      });
    }
  }

  /**
   * Create a case for ID verification document submission.
   * CRITICAL: throws on failure as caller needs the case ID for file attachment.
   *
   * @returns The Salesforce Case ID
   * @throws Error if case creation fails
   */
  async createIdVerificationCase(params: IdVerificationCaseParams): Promise<string> {
    const { accountId, filename, mimeType, sizeBytes } = params;

    const description = this.buildDescription([
      "Customer submitted their id card for verification.",
      "",
      `Document: ${filename || "residence-card"}`,
      `File Type: ${mimeType}`,
      `File Size: ${(sizeBytes / 1024).toFixed(2)} KB`,
      "",
      "The ID document is attached to this Case (see Files related list).",
    ]);

    try {
      const { id: caseId } = await this.caseService.createCase({
        accountId,
        subject: "ID verification review (Portal)",
        description,
        origin: SALESFORCE_CASE_ORIGIN.PORTAL_NOTIFICATION,
      });

      this.logger.log("Created ID verification case", {
        accountIdTail: accountId.slice(-4),
        caseIdTail: caseId.slice(-4),
        filename,
      });

      return caseId;
    } catch (error) {
      this.logger.error("Failed to create ID verification case", {
        accountIdTail: accountId.slice(-4),
        filename,
        error: extractErrorMessage(error),
      });
      throw new Error("Failed to create verification case");
    }
  }

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

  /**
   * Build a Salesforce Lightning URL for a record.
   * Returns null if instance URL is not available.
   */
  private buildLightningUrl(sobjectType: string, recordId: string): string | null {
    const instanceUrl = this.sfConnection.getInstanceUrl();
    if (!instanceUrl) return null;
    return `${instanceUrl}/lightning/r/${sobjectType}/${recordId}/view`;
  }

  /**
   * Build a case description from an array of lines.
   * Filters out null/undefined/empty values and joins with newlines.
   */
  private buildDescription(lines: Array<string | null | undefined>): string {
    return lines.filter(Boolean).join("\n");
  }

  /**
   * Format an address object into a single-line string.
   */
  private formatAddress(address: EligibilityCheckCaseParams["address"]): string {
    return [
      address.address1,
      address.address2,
      address.city,
      address.state,
      address.postcode,
      address.country,
    ]
      .filter(Boolean)
      .join(", ");
  }

  /**
   * Create an address key for Salesforce Flow duplicate detection.
   *
   * Format: "{postcode}:{streetAddress}"
   * Example: "1060045:1-5-3"
   *
   * @param postcode - ZIP code (e.g., "1060045")
   * @param streetAddress - Street address detail (e.g., "1-5-3")
   * @returns Combined address key, or null if either value is missing
   */
  private createAddressKey(
    postcode: string | undefined | null,
    streetAddress: string | undefined | null
  ): string | null {
    if (!postcode || !streetAddress) {
      return null;
    }
    return `${postcode}:${streetAddress}`;
  }
}
