import { Inject, Injectable } from "@nestjs/common";
import { Logger } from "nestjs-pino";

import {
  type GuestEligibilityRequest,
  type GuestEligibilityResponse,
} from "@customer-portal/domain/get-started";

import { DistributedLockService } from "@bff/infra/cache/distributed-lock.service.js";
import { SalesforceAccountService } from "@bff/integrations/salesforce/services/salesforce-account.service.js";
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { safeOperation, OperationCriticality } from "@bff/core/utils/safe-operation.util.js";
import {
  PORTAL_SOURCE_INTERNET_ELIGIBILITY,
  PORTAL_STATUS_NOT_YET,
} from "@bff/modules/auth/constants/portal.constants.js";
import { AddressWriterService } from "@bff/modules/address/address-writer.service.js";

import { GetStartedSessionService } from "../otp/get-started-session.service.js";
import { CreateEligibilityCaseStep } from "./steps/index.js";

/**
 * Remove undefined properties from an object (for exactOptionalPropertyTypes compatibility)
 */
function removeUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {
  return Object.fromEntries(
    Object.entries(obj).filter(([, value]) => value !== undefined)
  ) as Partial<T>;
}

/**
 * Guest Eligibility Workflow Service
 *
 * Handles guest eligibility checks — creates SF Account + eligibility case
 * without OTP verification. Uses shared CreateEligibilityCaseStep.
 */
@Injectable()
export class GuestEligibilityWorkflowService {
  constructor(
    private readonly sessionService: GetStartedSessionService,
    private readonly salesforceAccountService: SalesforceAccountService,
    private readonly salesforceFacade: SalesforceFacade,
    private readonly lockService: DistributedLockService,
    private readonly eligibilityCaseStep: CreateEligibilityCaseStep,
    private readonly addressWriter: AddressWriterService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Guest eligibility check — NO email verification required
   *
   * Allows users to check availability without verifying email first.
   * Creates SF Account + eligibility case immediately.
   */
  async execute(
    request: GuestEligibilityRequest,
    fingerprint?: string
  ): Promise<GuestEligibilityResponse> {
    const { email, firstName, lastName, address, phone, continueToAccount } = request;
    const normalizedEmail = email.toLowerCase().trim();

    this.logger.log(
      { email: normalizedEmail, continueToAccount, fingerprint },
      "Guest eligibility check initiated"
    );

    const lockKey = `guest-eligibility:${normalizedEmail}`;

    try {
      return await this.lockService.withLock(
        lockKey,
        async () => {
          // Check if SF account already exists for this email
          let sfAccountId: string;

          const existingSf = await this.salesforceAccountService.findByEmail(normalizedEmail);

          if (existingSf) {
            sfAccountId = existingSf.id;
            this.logger.log(
              { email: normalizedEmail, sfAccountId },
              "Using existing SF account for guest eligibility check"
            );

            await this.salesforceFacade.updateAccountPortalFields(sfAccountId, {
              status: PORTAL_STATUS_NOT_YET,
              source: PORTAL_SOURCE_INTERNET_ELIGIBILITY,
            });
          } else {
            const { accountId } = await this.salesforceAccountService.createAccount({
              firstName,
              lastName,
              email: normalizedEmail,
              phone: phone ?? "",
              portalSource: PORTAL_SOURCE_INTERNET_ELIGIBILITY,
            });
            sfAccountId = accountId;
            this.logger.log(
              { email: normalizedEmail, sfAccountId },
              "Created SF account for guest eligibility check"
            );
          }

          // SF address write (DEGRADABLE) + eligibility case creation — independent, run in parallel
          const [, { caseId }] = await Promise.all([
            safeOperation(async () => this.addressWriter.writeToSalesforce(sfAccountId, address), {
              criticality: OperationCriticality.OPTIONAL,
              fallback: undefined,
              context: "SF address write",
              logger: this.logger,
              metadata: { email: normalizedEmail },
            }),
            this.eligibilityCaseStep.execute({
              sfAccountId,
              address: this.addressWriter.toCaseAddress(address),
            }),
          ]);

          // If user wants to continue to account creation, generate a handoff token
          let handoffToken: string | undefined;
          if (continueToAccount) {
            handoffToken = await this.sessionService.createGuestHandoffToken(normalizedEmail, {
              firstName,
              lastName,
              address: removeUndefined(address),
              ...(phone && { phone }),
              sfAccountId,
            });
            this.logger.debug(
              { email: normalizedEmail, handoffToken },
              "Created handoff token for account creation"
            );
          }

          return {
            submitted: true,
            requestId: caseId,
            sfAccountId,
            handoffToken,
            message: "Eligibility check submitted. We'll notify you of the results.",
          };
        },
        { ttlMs: 30_000 }
      );
    } catch (error) {
      this.logger.error(
        { error: extractErrorMessage(error), email: normalizedEmail },
        "Guest eligibility check failed"
      );

      return {
        submitted: false,
        message: "Failed to submit eligibility check. Please try again.",
      };
    }
  }
}
