import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import type { SalesforceOrderRecord } from "@customer-portal/domain/orders/providers";
import type { ContactIdentityData } from "./sim-fulfillment.service.js";

/**
 * Fulfillment Context Mapper Service
 *
 * Extracts and transforms configuration data from payload and Salesforce order records.
 * Used during fulfillment to prepare data for SIM activation and other operations.
 */
@Injectable()
export class FulfillmentContextMapper {
  constructor(@Inject(Logger) private readonly logger: Logger) {}

  /**
   * Extract configurations from payload and Salesforce order
   *
   * Merges payload configurations with Salesforce order fields,
   * with payload taking precedence over SF fields.
   */
  extractConfigurations(
    rawConfigurations: unknown,
    sfOrder?: SalesforceOrderRecord | null
  ): Record<string, unknown> {
    const config: Record<string, unknown> = {};

    // Start with payload configurations if provided
    if (rawConfigurations && typeof rawConfigurations === "object") {
      Object.assign(config, rawConfigurations as Record<string, unknown>);
    }

    // Fill in missing fields from Salesforce order (CDC flow fallback)
    if (sfOrder) {
      if (!config["simType"] && sfOrder.SIM_Type__c) {
        config["simType"] = sfOrder.SIM_Type__c;
      }
      if (!config["eid"] && sfOrder.EID__c) {
        config["eid"] = sfOrder.EID__c;
      }
      if (!config["activationType"] && sfOrder.Activation_Type__c) {
        config["activationType"] = sfOrder.Activation_Type__c;
      }
      if (!config["scheduledAt"] && sfOrder.Activation_Scheduled_At__c) {
        config["scheduledAt"] = sfOrder.Activation_Scheduled_At__c;
      }
      if (!config["mnpPhone"] && sfOrder.MNP_Phone_Number__c) {
        config["mnpPhone"] = sfOrder.MNP_Phone_Number__c;
      }
      // MNP fields
      if (!config["isMnp"] && sfOrder.MNP_Application__c) {
        config["isMnp"] = sfOrder.MNP_Application__c ? "true" : undefined;
      }
      if (!config["mnpNumber"] && sfOrder.MNP_Reservation_Number__c) {
        config["mnpNumber"] = sfOrder.MNP_Reservation_Number__c;
      }
      if (!config["mnpExpiry"] && sfOrder.MNP_Expiry_Date__c) {
        config["mnpExpiry"] = sfOrder.MNP_Expiry_Date__c;
      }
      if (!config["mvnoAccountNumber"] && sfOrder.MVNO_Account_Number__c) {
        config["mvnoAccountNumber"] = sfOrder.MVNO_Account_Number__c;
      }
      if (!config["portingFirstName"] && sfOrder.Porting_FirstName__c) {
        config["portingFirstName"] = sfOrder.Porting_FirstName__c;
      }
      if (!config["portingLastName"] && sfOrder.Porting_LastName__c) {
        config["portingLastName"] = sfOrder.Porting_LastName__c;
      }
      if (!config["portingFirstNameKatakana"] && sfOrder.Porting_FirstName_Katakana__c) {
        config["portingFirstNameKatakana"] = sfOrder.Porting_FirstName_Katakana__c;
      }
      if (!config["portingLastNameKatakana"] && sfOrder.Porting_LastName_Katakana__c) {
        config["portingLastNameKatakana"] = sfOrder.Porting_LastName_Katakana__c;
      }
      if (!config["portingGender"] && sfOrder.Porting_Gender__c) {
        config["portingGender"] = sfOrder.Porting_Gender__c;
      }
      if (!config["portingDateOfBirth"] && sfOrder.Porting_DateOfBirth__c) {
        config["portingDateOfBirth"] = sfOrder.Porting_DateOfBirth__c;
      }
    }

    return config;
  }

  /**
   * Extract contact identity data from Salesforce order porting fields
   *
   * Used for PA05-05 Voice Options Registration which requires:
   * - Name in Kanji and Kana
   * - Gender (M/F)
   * - Birthday (YYYYMMDD)
   *
   * Returns undefined if required fields are missing.
   */
  extractContactIdentity(sfOrder?: SalesforceOrderRecord | null): ContactIdentityData | undefined {
    if (!sfOrder) return undefined;

    // Extract porting fields
    const firstnameKanji = sfOrder.Porting_FirstName__c;
    const lastnameKanji = sfOrder.Porting_LastName__c;
    const firstnameKana = sfOrder.Porting_FirstName_Katakana__c;
    const lastnameKana = sfOrder.Porting_LastName_Katakana__c;
    const genderRaw = sfOrder.Porting_Gender__c;
    const birthdayRaw = sfOrder.Porting_DateOfBirth__c;

    // Validate all required fields are present
    if (!firstnameKanji || !lastnameKanji) {
      this.logger.debug("Missing name fields for contact identity", {
        hasFirstName: !!firstnameKanji,
        hasLastName: !!lastnameKanji,
      });
      return undefined;
    }

    if (!firstnameKana || !lastnameKana) {
      this.logger.debug("Missing kana name fields for contact identity", {
        hasFirstNameKana: !!firstnameKana,
        hasLastNameKana: !!lastnameKana,
      });
      return undefined;
    }

    // Validate gender (must be M or F)
    const gender = genderRaw === "M" || genderRaw === "F" ? genderRaw : undefined;
    if (!gender) {
      this.logger.debug("Invalid or missing gender for contact identity", { genderRaw });
      return undefined;
    }

    // Format birthday to YYYYMMDD
    const birthday = this.formatBirthdayToYYYYMMDD(birthdayRaw);
    if (!birthday) {
      this.logger.debug("Invalid or missing birthday for contact identity", { birthdayRaw });
      return undefined;
    }

    return {
      firstnameKanji,
      lastnameKanji,
      firstnameKana,
      lastnameKana,
      gender,
      birthday,
    };
  }

  /**
   * Format birthday from various formats to YYYYMMDD
   */
  formatBirthdayToYYYYMMDD(dateStr?: string | null): string | undefined {
    if (!dateStr) return undefined;

    // If already in YYYYMMDD format
    if (/^\d{8}$/.test(dateStr)) {
      return dateStr;
    }

    // Try parsing as ISO date (YYYY-MM-DD)
    const isoMatch = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
    if (isoMatch) {
      return `${isoMatch[1]}${isoMatch[2]}${isoMatch[3]}`;
    }

    // Try parsing as Date object
    try {
      const date = new Date(dateStr);
      if (!Number.isNaN(date.getTime())) {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, "0");
        const day = String(date.getDate()).padStart(2, "0");
        return `${year}${month}${day}`;
      }
    } catch {
      // Parsing failed
    }

    return undefined;
  }
}
