/**
 * Opportunity Query Service
 *
 * Handles all read/lookup operations for Salesforce Opportunities.
 * Extracted from SalesforceOpportunityService for single-responsibility.
 */

import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { SalesforceConnection } from "../salesforce-connection.service.js";
import { assertSalesforceId } from "../../utils/soql.util.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import type { SalesforceResponse } from "@customer-portal/domain/common/providers";
import {
  type OpportunityStageValue,
  type OpportunityProductTypeValue,
  type CancellationNoticeValue,
  type LineReturnStatusValue,
  type CommodityTypeValue,
  type ApplicationStageValue,
  type OpportunitySourceValue,
  type OpportunityRecord,
  OPPORTUNITY_STAGE,
  OPEN_OPPORTUNITY_STAGES,
  COMMODITY_TYPE,
  OPPORTUNITY_PRODUCT_TYPE,
  getCommodityTypeProductType,
} from "@customer-portal/domain/opportunity";
import {
  OPPORTUNITY_FIELD_MAP,
  OPPORTUNITY_MATCH_QUERY_FIELDS,
  OPPORTUNITY_DETAIL_QUERY_FIELDS,
  OPPORTUNITY_INTERNET_CANCELLATION_QUERY_FIELDS,
  OPPORTUNITY_SIM_CANCELLATION_QUERY_FIELDS,
} from "../../constants/index.js";
import type { SalesforceOpportunityRecord } from "./opportunity.types.js";

export interface InternetCancellationStatusResult {
  stage: OpportunityStageValue;
  isPending: boolean;
  isComplete: boolean;
  scheduledEndDate?: string | undefined;
  rentalReturnStatus?: LineReturnStatusValue | undefined;
}

export interface SimCancellationStatusResult {
  stage: OpportunityStageValue;
  isPending: boolean;
  isComplete: boolean;
  scheduledEndDate?: string | undefined;
}

@Injectable()
export class OpportunityQueryService {
  constructor(
    private readonly sf: SalesforceConnection,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Find an open Opportunity for an account by product type
   */
  async findOpenOpportunityForAccount(
    accountId: string,
    productType: OpportunityProductTypeValue,
    options?: { stages?: OpportunityStageValue[] }
  ): Promise<string | null> {
    const safeAccountId = assertSalesforceId(accountId, "accountId");

    const commodityTypeValues = this.getCommodityTypesForProductType(productType);

    const stages =
      Array.isArray(options?.stages) && options?.stages.length > 0
        ? options.stages
        : OPEN_OPPORTUNITY_STAGES;

    this.logger.debug("Looking for open Opportunity", {
      accountId: safeAccountId,
      productType,
      commodityTypes: commodityTypeValues,
      stages,
    });

    const stageList = stages.map((s: OpportunityStageValue) => `'${s}'`).join(", ");
    const commodityTypeList = commodityTypeValues.map(ct => `'${ct}'`).join(", ");

    const soql = `
      SELECT ${OPPORTUNITY_MATCH_QUERY_FIELDS.join(", ")}
      FROM Opportunity
      WHERE ${OPPORTUNITY_FIELD_MAP.accountId} = '${safeAccountId}'
        AND ${OPPORTUNITY_FIELD_MAP.commodityType} IN (${commodityTypeList})
        AND ${OPPORTUNITY_FIELD_MAP.stage} IN (${stageList})
        AND ${OPPORTUNITY_FIELD_MAP.isClosed} = false
      ORDER BY CreatedDate DESC
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:findOpenForAccount",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];

      if (record) {
        this.logger.debug("Found open Opportunity", {
          opportunityId: record.Id,
          stage: record.StageName,
          productType,
        });
        return record.Id;
      }

      this.logger.debug("No open Opportunity found", {
        accountId: safeAccountId,
        productType,
      });
      return null;
    } catch (error) {
      const errorDetails: Record<string, unknown> = {
        error: extractErrorMessage(error),
        accountId: safeAccountId,
        productType,
      };

      if (error && typeof error === "object") {
        const err = error as Record<string, unknown>;
        if (err["errorCode"]) errorDetails["errorCode"] = err["errorCode"];
        if (err["message"]) errorDetails["rawMessage"] = err["message"];
      }

      this.logger.error(errorDetails, "Failed to find open Opportunity");
      return null;
    }
  }

  /**
   * Find Opportunity linked to an Order
   */
  async findOpportunityByOrderId(orderId: string): Promise<string | null> {
    const safeOrderId = assertSalesforceId(orderId, "orderId");

    this.logger.debug("Looking for Opportunity by Order ID", {
      orderId: safeOrderId,
    });

    const soql = `
      SELECT OpportunityId
      FROM Order
      WHERE Id = '${safeOrderId}'
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:findByOrderId",
      })) as SalesforceResponse<{ OpportunityId?: string }>;

      const record = result.records?.[0];
      const opportunityId = record?.OpportunityId;

      if (opportunityId) {
        this.logger.debug("Found Opportunity for Order", {
          orderId: safeOrderId,
          opportunityId,
        });
        return opportunityId;
      }

      return null;
    } catch (error) {
      this.logger.error("Failed to find Opportunity by Order ID", {
        error: extractErrorMessage(error),
        orderId: safeOrderId,
      });
      return null;
    }
  }

  /**
   * Find Opportunity by WHMCS Service ID
   */
  async findOpportunityByWhmcsServiceId(whmcsServiceId: number): Promise<string | null> {
    this.logger.debug("Looking for Opportunity by WHMCS Service ID", {
      whmcsServiceId,
    });

    const soql = `
      SELECT Id, ${OPPORTUNITY_FIELD_MAP.stage}
      FROM Opportunity
      WHERE ${OPPORTUNITY_FIELD_MAP.whmcsServiceId} = ${whmcsServiceId}
      ORDER BY CreatedDate DESC
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:findByWhmcsServiceId",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];

      if (record) {
        this.logger.debug("Found Opportunity for WHMCS Service", {
          opportunityId: record.Id,
          whmcsServiceId,
        });
        return record.Id;
      }

      return null;
    } catch (error) {
      this.logger.error("Failed to find Opportunity by WHMCS Service ID", {
        error: extractErrorMessage(error),
        whmcsServiceId,
      });
      return null;
    }
  }

  /**
   * Get full Opportunity details by ID
   */
  async getOpportunityById(opportunityId: string): Promise<OpportunityRecord | null> {
    const safeOppId = assertSalesforceId(opportunityId, "opportunityId");

    const soql = `
      SELECT ${OPPORTUNITY_DETAIL_QUERY_FIELDS.join(", ")}
      FROM Opportunity
      WHERE Id = '${safeOppId}'
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:getById",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];

      if (!record) {
        return null;
      }

      return this.transformToOpportunityRecord(record);
    } catch (error) {
      this.logger.error("Failed to get Opportunity by ID", {
        error: extractErrorMessage(error),
        opportunityId: safeOppId,
      });
      return null;
    }
  }

  /**
   * Get Internet cancellation status by WHMCS Service ID
   */
  async getInternetCancellationStatus(
    whmcsServiceId: number
  ): Promise<InternetCancellationStatusResult | null> {
    const soql = `
      SELECT ${OPPORTUNITY_INTERNET_CANCELLATION_QUERY_FIELDS.join(", ")}
      FROM Opportunity
      WHERE ${OPPORTUNITY_FIELD_MAP.whmcsServiceId} = ${whmcsServiceId}
      ORDER BY CreatedDate DESC
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:getInternetCancellationStatus",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];
      if (!record) return null;

      return this.buildInternetCancellationResult(record);
    } catch (error) {
      this.logger.error("Failed to get Internet cancellation status", {
        error: extractErrorMessage(error),
        whmcsServiceId,
      });
      return null;
    }
  }

  /**
   * Get Internet cancellation status by Opportunity ID
   */
  async getInternetCancellationStatusByOpportunityId(
    opportunityId: string
  ): Promise<InternetCancellationStatusResult | null> {
    const safeOppId = assertSalesforceId(opportunityId, "opportunityId");
    const soql = `
      SELECT ${OPPORTUNITY_INTERNET_CANCELLATION_QUERY_FIELDS.join(", ")}
      FROM Opportunity
      WHERE Id = '${safeOppId}'
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:getInternetCancellationStatusByOpportunityId",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];
      if (!record) return null;

      return this.buildInternetCancellationResult(record);
    } catch (error) {
      this.logger.error("Failed to get Internet cancellation status by Opportunity ID", {
        error: extractErrorMessage(error),
        opportunityId: safeOppId,
      });
      return null;
    }
  }

  /**
   * Get SIM cancellation status by WHMCS Service ID
   */
  async getSimCancellationStatus(
    whmcsServiceId: number
  ): Promise<SimCancellationStatusResult | null> {
    const soql = `
      SELECT ${OPPORTUNITY_SIM_CANCELLATION_QUERY_FIELDS.join(", ")}
      FROM Opportunity
      WHERE ${OPPORTUNITY_FIELD_MAP.whmcsServiceId} = ${whmcsServiceId}
      ORDER BY CreatedDate DESC
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:getSimCancellationStatus",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];
      if (!record) return null;

      return this.buildSimCancellationResult(record);
    } catch (error) {
      this.logger.error("Failed to get SIM cancellation status", {
        error: extractErrorMessage(error),
        whmcsServiceId,
      });
      return null;
    }
  }

  /**
   * Get SIM cancellation status by Opportunity ID
   */
  async getSimCancellationStatusByOpportunityId(
    opportunityId: string
  ): Promise<SimCancellationStatusResult | null> {
    const safeOppId = assertSalesforceId(opportunityId, "opportunityId");
    const soql = `
      SELECT ${OPPORTUNITY_SIM_CANCELLATION_QUERY_FIELDS.join(", ")}
      FROM Opportunity
      WHERE Id = '${safeOppId}'
      LIMIT 1
    `;

    try {
      const result = (await this.sf.query(soql, {
        label: "opportunity:getSimCancellationStatusByOpportunityId",
      })) as SalesforceResponse<SalesforceOpportunityRecord>;

      const record = result.records?.[0];
      if (!record) return null;

      return this.buildSimCancellationResult(record);
    } catch (error) {
      this.logger.error("Failed to get SIM cancellation status by Opportunity ID", {
        error: extractErrorMessage(error),
        opportunityId: safeOppId,
      });
      return null;
    }
  }

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

  private getCommodityTypesForProductType(
    productType: OpportunityProductTypeValue
  ): CommodityTypeValue[] {
    switch (productType) {
      case OPPORTUNITY_PRODUCT_TYPE.INTERNET:
        return [COMMODITY_TYPE.PERSONAL_HOME_INTERNET, COMMODITY_TYPE.CORPORATE_HOME_INTERNET];
      case OPPORTUNITY_PRODUCT_TYPE.SIM:
        return [COMMODITY_TYPE.SIM];
      case OPPORTUNITY_PRODUCT_TYPE.VPN:
        return [COMMODITY_TYPE.VPN];
      default:
        return [];
    }
  }

  private buildInternetCancellationResult(
    record: SalesforceOpportunityRecord
  ): InternetCancellationStatusResult {
    const stage = record.StageName as OpportunityStageValue;
    return {
      stage,
      isPending: stage === OPPORTUNITY_STAGE.CANCELLING,
      isComplete: stage === OPPORTUNITY_STAGE.CANCELLED,
      scheduledEndDate: record.ScheduledCancellationDateAndTime__c,
      rentalReturnStatus: record.LineReturn__c as LineReturnStatusValue | undefined,
    };
  }

  private buildSimCancellationResult(
    record: SalesforceOpportunityRecord
  ): SimCancellationStatusResult {
    const stage = record.StageName as OpportunityStageValue;
    return {
      stage,
      isPending: stage === OPPORTUNITY_STAGE.CANCELLING,
      isComplete: stage === OPPORTUNITY_STAGE.CANCELLED,
      scheduledEndDate: record.SIMScheduledCancellationDateAndTime__c,
    };
  }

  private transformToOpportunityRecord(record: SalesforceOpportunityRecord): OpportunityRecord {
    const commodityType = record.CommodityType as CommodityTypeValue | undefined;
    const productType = commodityType ? getCommodityTypeProductType(commodityType) : undefined;

    return {
      id: record.Id,
      name: record.Name,
      accountId: record.AccountId,
      stage: record.StageName as OpportunityStageValue,
      closeDate: record.CloseDate,
      commodityType,
      productType: productType ?? undefined,
      source: record.Opportunity_Source__c as OpportunitySourceValue | undefined,
      applicationStage: record.Application_Stage__c as ApplicationStageValue | undefined,
      isClosed: record.IsClosed,
      whmcsServiceId: record.WHMCS_Service_ID__c,
      scheduledCancellationDate: record.ScheduledCancellationDateAndTime__c,
      cancellationNotice: record.CancellationNotice__c as CancellationNoticeValue | undefined,
      lineReturnStatus: record.LineReturn__c as LineReturnStatusValue | undefined,
      createdDate: record.CreatedDate,
      lastModifiedDate: record.LastModifiedDate,
    };
  }
}
