/**
 * Service responsible for validating signup data and checking for existing accounts
 */
import { Injectable, Inject, BadRequestException, ConflictException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import type { Request } from "express";
import { AuditService, AuditAction } from "@bff/infra/audit/audit.service.js";
import { UsersService } from "@bff/modules/users/application/users.service.js";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
import { WhmcsAccountDiscoveryService } from "@bff/integrations/whmcs/services/whmcs-account-discovery.service.js";
import { SalesforceAccountService } from "@bff/integrations/salesforce/services/salesforce-account.service.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { SignupAccountResolverService } from "./signup-account-resolver.service.js";
import type { ValidateSignupRequest } from "@customer-portal/domain/auth";
import type { SignupPreflightResult } from "./signup.types.js";

@Injectable()
export class SignupValidationService {
  constructor(
    private readonly usersService: UsersService,
    private readonly mappingsService: MappingsService,
    private readonly discoveryService: WhmcsAccountDiscoveryService,
    private readonly salesforceAccountService: SalesforceAccountService,
    private readonly accountResolver: SignupAccountResolverService,
    private readonly auditService: AuditService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Validate customer number for signup
   */
  async validateCustomerNumber(
    validateData: ValidateSignupRequest,
    request?: Request
  ): Promise<{ valid: boolean; sfAccountId?: string; message: string }> {
    const { sfNumber } = validateData;
    const normalizedCustomerNumber = this.accountResolver.normalizeCustomerNumber(sfNumber);

    if (!normalizedCustomerNumber) {
      await this.auditService.logAuthEvent(
        AuditAction.SIGNUP,
        undefined,
        { sfNumber: sfNumber ?? null, reason: "no_customer_number_provided" },
        request,
        true
      );

      return {
        valid: true,
        message: "Customer number is not required for signup",
      };
    }

    try {
      const accountSnapshot =
        await this.accountResolver.getAccountSnapshot(normalizedCustomerNumber);
      if (!accountSnapshot) {
        await this.auditService.logAuthEvent(
          AuditAction.SIGNUP,
          undefined,
          { sfNumber: normalizedCustomerNumber, reason: "SF number not found" },
          request,
          false,
          "Customer number not found in Salesforce"
        );
        throw new BadRequestException("Customer number not found in Salesforce");
      }

      const existingMapping = await this.mappingsService.findBySfAccountId(accountSnapshot.id);
      if (existingMapping) {
        await this.auditService.logAuthEvent(
          AuditAction.SIGNUP,
          undefined,
          { sfNumber, sfAccountId: accountSnapshot.id, reason: "Already has mapping" },
          request,
          false,
          "Customer number already registered"
        );
        throw new ConflictException(
          "You already have an account. Please use the login page to access your existing account."
        );
      }

      if (accountSnapshot.WH_Account__c && accountSnapshot.WH_Account__c.trim() !== "") {
        await this.auditService.logAuthEvent(
          AuditAction.SIGNUP,
          undefined,
          {
            sfNumber,
            sfAccountId: accountSnapshot.id,
            whAccount: accountSnapshot.WH_Account__c,
            reason: "WH Account not empty",
          },
          request,
          false,
          "Account already has WHMCS integration"
        );
        throw new ConflictException(
          "You already have an account. Please use the login page to access your existing account."
        );
      }

      await this.auditService.logAuthEvent(
        AuditAction.SIGNUP,
        undefined,
        { sfNumber: normalizedCustomerNumber, sfAccountId: accountSnapshot.id, step: "validation" },
        request,
        true
      );

      return {
        valid: true,
        sfAccountId: accountSnapshot.id,
        message: "Customer number validated successfully",
      };
    } catch (error) {
      if (error instanceof BadRequestException || error instanceof ConflictException) {
        throw error;
      }

      await this.auditService.logAuthEvent(
        AuditAction.SIGNUP,
        undefined,
        { sfNumber: normalizedCustomerNumber, error: extractErrorMessage(error) },
        request,
        false,
        extractErrorMessage(error)
      );

      this.logger.error("Signup validation error", { error: extractErrorMessage(error) });
      throw new BadRequestException("Validation failed");
    }
  }

  /**
   * Check if email is already registered in portal
   */
  async checkExistingPortalUser(email: string): Promise<{
    exists: boolean;
    userId?: string;
    hasMapping?: boolean;
    needsPasswordSet?: boolean;
  }> {
    const normalizedEmail = email.toLowerCase().trim();
    const existingUser = await this.usersService.findByEmailInternal(normalizedEmail);

    if (!existingUser) {
      return { exists: false };
    }

    const hasMapping = await this.mappingsService.hasMapping(existingUser.id);
    return {
      exists: true,
      userId: existingUser.id,
      hasMapping,
      needsPasswordSet: !existingUser.passwordHash,
    };
  }

  /**
   * Comprehensive preflight check before signup
   */
  async preflightCheck(email: string, sfNumber?: string | null): Promise<SignupPreflightResult> {
    const normalizedEmail = email.toLowerCase().trim();
    const normalizedCustomerNumber = this.accountResolver.normalizeCustomerNumber(sfNumber);

    const result: SignupPreflightResult = {
      canProceed: false,
      nextAction: null,
      messages: [],
      normalized: { email: normalizedEmail },
      portal: { userExists: false },
      salesforce: { alreadyMapped: false },
      whmcs: { clientExists: false },
    };

    // Check portal user
    const portalUserAuth = await this.usersService.findByEmailInternal(normalizedEmail);
    if (portalUserAuth) {
      result.portal.userExists = true;
      const mapped = await this.mappingsService.hasMapping(portalUserAuth.id);
      if (mapped) {
        result.nextAction = "login";
        result.messages.push("An account already exists. Please sign in.");
        return result;
      }

      result.portal.needsPasswordSet = !portalUserAuth.passwordHash;
      result.nextAction = portalUserAuth.passwordHash ? "login" : "fix_input";
      result.messages.push(
        portalUserAuth.passwordHash
          ? "An account exists without billing link. Please sign in to continue setup."
          : "An account exists and needs password setup. Please set a password to continue."
      );
      return result;
    }

    // Check without customer number
    if (!normalizedCustomerNumber) {
      const client = await this.discoveryService.findClientByEmail(normalizedEmail);
      if (client) {
        result.whmcs.clientExists = true;
        result.whmcs.clientId = client.id;

        try {
          const mapped = await this.mappingsService.findByWhmcsClientId(client.id);
          if (mapped) {
            result.nextAction = "login";
            result.messages.push("This billing account is already linked. Please sign in.");
            return result;
          }
        } catch {
          // ignore; treat as unmapped
        }

        result.nextAction = "link_whmcs";
        result.messages.push(
          "We found an existing billing account for this email. Please transfer your account to continue."
        );
        return result;
      }

      try {
        const existingSf = await this.salesforceAccountService.findByEmail(normalizedEmail);
        if (existingSf) {
          // SF account exists without WHMCS - allow account completion via get-started flow
          result.salesforce.accountId = existingSf.id;
          result.nextAction = "complete_account";
          result.messages.push(
            "We found your existing account. Please verify your email to complete setup."
          );
          return result;
        }
      } catch (sfErr) {
        this.logger.warn("Salesforce preflight check failed", {
          error: extractErrorMessage(sfErr),
        });
      }

      result.canProceed = true;
      result.nextAction = "proceed_signup";
      result.messages.push("All checks passed. Ready to create your account.");
      return result;
    }

    // Check with customer number
    const accountSnapshot = await this.accountResolver.getAccountSnapshot(normalizedCustomerNumber);
    if (!accountSnapshot) {
      result.nextAction = "fix_input";
      result.messages.push("Customer number not found in Salesforce");
      return result;
    }
    result.salesforce.accountId = accountSnapshot.id;

    const existingMapping = await this.mappingsService.findBySfAccountId(accountSnapshot.id);
    if (existingMapping) {
      result.salesforce.alreadyMapped = true;
      result.nextAction = "login";
      result.messages.push("This customer number is already registered. Please sign in.");
      return result;
    }

    const client = await this.discoveryService.findClientByEmail(normalizedEmail);
    if (client) {
      result.whmcs.clientExists = true;
      result.whmcs.clientId = client.id;

      try {
        const mapped = await this.mappingsService.findByWhmcsClientId(client.id);
        if (mapped) {
          result.nextAction = "login";
          result.messages.push("This billing account is already linked. Please sign in.");
          return result;
        }
      } catch {
        // ignore; treat as unmapped
      }

      result.nextAction = "link_whmcs";
      result.messages.push(
        "We found an existing billing account for this email. Please transfer your account to continue."
      );
      return result;
    }

    result.canProceed = true;
    result.nextAction = "proceed_signup";
    result.messages.push("All checks passed. Ready to create your account.");
    return result;
  }
}
