import {
  BadRequestException,
  ConflictException,
  Inject,
  Injectable,
  UnauthorizedException,
} from "@nestjs/common";
import { Logger } from "nestjs-pino";
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 { WhmcsClientService } from "@bff/integrations/whmcs/services/whmcs-client.service.js";
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { mapPrismaUserToDomain } from "@bff/infra/mappers/index.js";
import { getCustomFieldValue } from "@customer-portal/domain/customer/providers";
import { withErrorHandling } from "@bff/core/utils/error-handler.util.js";
import type { User } from "@customer-portal/domain/customer";
import {
  PORTAL_SOURCE_MIGRATED,
  PORTAL_STATUS_ACTIVE,
} from "@bff/modules/auth/constants/portal.constants.js";

@Injectable()
export class WhmcsLinkWorkflowService {
  constructor(
    private readonly usersService: UsersService,
    private readonly mappingsService: MappingsService,
    private readonly whmcsClientService: WhmcsClientService,
    private readonly discoveryService: WhmcsAccountDiscoveryService,
    private readonly salesforceService: SalesforceFacade,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  async linkWhmcsUser(email: string, password: string) {
    const existingUser = await this.usersService.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."
      );
    }

    return withErrorHandling(
      async () => {
        const clientDetails = await this.discoveryService.findClientByEmail(email);
        if (!clientDetails) {
          throw new BadRequestException(
            "No billing account found with this email address. Please check your email or contact support."
          );
        }

        const clientNumericId = clientDetails.id;

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

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

        const customerNumber =
          getCustomFieldValue(clientDetails.customfields, "198")?.trim() ??
          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,
        });

        const sfAccount = await this.salesforceService.findAccountByCustomerNumber(customerNumber);
        if (!sfAccount) {
          throw new BadRequestException("Salesforce account not found. Please contact support.");
        }

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

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

        const prismaUser = await this.usersService.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: extractErrorMessage(error),
          });
        }

        return {
          user: userProfile,
          needsPasswordSet: true,
        };
      },
      this.logger,
      {
        context: "WHMCS account linking",
        fallbackMessage: "Failed to link WHMCS account",
        rethrow: [BadRequestException, ConflictException, UnauthorizedException],
      }
    );
  }
}
