import {
  BadRequestException,
  ConflictException,
  Inject,
  Injectable,
  UnauthorizedException,
  NotFoundException,
} from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { UsersFacade } from "@bff/modules/users/application/users.facade";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { mapPrismaUserToDomain } from "@bff/infra/mappers";
import { Providers as CustomerProviders } from "@customer-portal/domain/customer";
import type { User } from "@customer-portal/domain/customer";
import {
  PORTAL_SOURCE_MIGRATED,
  PORTAL_STATUS_ACTIVE,
} from "@bff/modules/auth/constants/portal.constants";

@Injectable()
export class WhmcsLinkWorkflowService {
  constructor(
    private readonly usersFacade: UsersFacade,
    private readonly mappingsService: MappingsService,
    private readonly whmcsService: WhmcsService,
    private readonly salesforceService: SalesforceService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  async linkWhmcsUser(email: string, password: string) {
    const existingUser = await this.usersFacade.findByEmailInternal(email);
    if (existingUser) {
      if (!existingUser.passwordHash) {
        this.logger.log("User exists but has no password - allowing password setup to continue", {
          userId: existingUser.id,
        });
        return {
          user: mapPrismaUserToDomain(existingUser),
          needsPasswordSet: true,
        };
      }

      throw new ConflictException(
        "User already exists in portal and has completed setup. Please use the login page."
      );
    }

    try {
      let clientDetails; // Type inferred from WHMCS service
      try {
        clientDetails = await this.whmcsService.getClientDetailsByEmail(email);
      } catch (error) {
        this.logger.error("WHMCS client lookup failed", {
          error: getErrorMessage(error),
          email, // Safe to log email for debugging since it's not sensitive
        });

        // Provide more specific error messages based on the error type
        if (error instanceof Error && error.message.includes("not found")) {
          throw new UnauthorizedException(
            "No billing account found with this email address. Please check your email or contact support."
          );
        }

        throw new UnauthorizedException("Unable to verify account. Please try again later.");
      }

      const clientNumericId = clientDetails.id;

      try {
        const existingMapping = await this.mappingsService.findByWhmcsClientId(clientNumericId);
        if (existingMapping) {
          throw new ConflictException("This billing account is already linked. Please sign in.");
        }
      } catch (mapErr) {
        if (mapErr instanceof ConflictException) throw mapErr;
      }

      try {
        this.logger.debug("Validating WHMCS credentials");
        const validateResult = await this.whmcsService.validateLogin(email, password);
        this.logger.debug("WHMCS validation successful");
        if (!validateResult || !validateResult.userId) {
          throw new UnauthorizedException("Invalid email or password. Please try again.");
        }
      } catch (error) {
        if (error instanceof UnauthorizedException) throw error;

        const errorMessage = getErrorMessage(error);
        this.logger.error("WHMCS credential validation failed", { error: errorMessage });

        // Check if this is a WHMCS authentication error and provide user-friendly message
        const normalizedMessage = errorMessage.toLowerCase();
        const authErrorPhrases = [
          "email or password invalid",
          "invalid email or password",
          "authentication failed",
          "login failed",
        ];
        if (authErrorPhrases.some(phrase => normalizedMessage.includes(phrase))) {
          throw new UnauthorizedException(
            "Invalid email or password. Please check your credentials and try again."
          );
        }

        // For other errors, provide generic message to avoid exposing system details
        throw new UnauthorizedException("Unable to verify credentials. Please try again later.");
      }

      const customerNumber =
        CustomerProviders.Whmcs.getCustomFieldValue(clientDetails.customfields, "198")?.trim() ??
        CustomerProviders.Whmcs.getCustomFieldValue(
          clientDetails.customfields,
          "Customer Number"
        )?.trim();

      if (!customerNumber) {
        throw new BadRequestException(
          `Customer Number not found in WHMCS custom field 198. Please contact support.`
        );
      }

      this.logger.log("Found Customer Number for WHMCS client", {
        whmcsClientId: clientNumericId,
        hasCustomerNumber: !!customerNumber,
      });

      let sfAccount: { id: string } | null;
      try {
        sfAccount = await this.salesforceService.findAccountByCustomerNumber(customerNumber);
        if (!sfAccount) {
          throw new BadRequestException("Salesforce account not found. Please contact support.");
        }
      } catch (error) {
        if (error instanceof BadRequestException) throw error;
        this.logger.error("Salesforce account lookup failed", { error: getErrorMessage(error) });
        throw new BadRequestException(
          "Unable to verify customer information. Please contact support."
        );
      }

      const createdUser = await this.usersFacade.create(
        {
          email,
          passwordHash: null,
          emailVerified: true,
        },
        { includeProfile: false }
      );

      await this.mappingsService.createMapping({
        userId: createdUser.id,
        whmcsClientId: clientNumericId,
        sfAccountId: sfAccount.id,
      });

      const prismaUser = await this.usersFacade.findByIdInternal(createdUser.id);
      if (!prismaUser) {
        throw new Error("Failed to load newly linked user");
      }

      const userProfile: User = mapPrismaUserToDomain(prismaUser);

      try {
        await this.salesforceService.updateAccountPortalFields(sfAccount.id, {
          status: PORTAL_STATUS_ACTIVE,
          source: PORTAL_SOURCE_MIGRATED,
          lastSignedInAt: new Date(),
        });
      } catch (error) {
        this.logger.warn("Failed to update Salesforce portal flags after WHMCS link", {
          accountId: sfAccount.id,
          error: getErrorMessage(error),
        });
      }

      return {
        user: userProfile,
        needsPasswordSet: true,
      };
    } catch (error) {
      const message = getErrorMessage(error);
      this.logger.error("WHMCS linking error", { error: message });

      // Preserve known auth/validation errors so the client gets actionable feedback
      if (error instanceof BadRequestException || error instanceof UnauthorizedException) {
        throw error;
      }

      // Treat missing WHMCS mappings/records as an auth-style failure rather than a system error
      if (
        error instanceof NotFoundException ||
        /whmcs client mapping not found/i.test(message) ||
        /whmcs.*not found/i.test(message)
      ) {
        throw new UnauthorizedException(
          "No billing account found with this email address. Please check your email or contact support."
        );
      }

      throw new BadRequestException("Failed to link WHMCS account");
    }
  }
}
