import { v4 as uuidv4 } from 'uuid';
import { Types } from 'mongoose';

import { OtpRecord, OtpPayload } from '@/modules/otp/otp.interface';
import { Otp } from '@/modules/otp/otp.model';
import { ApiError } from '@/shared/utils/errors';

import responseCodes from '@/shared/utils/responseCode/responseCode';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { generateFourDigitRandomNumber } from '@/shared/utils/generateRandomValues';
import { sendFast2smsMessage } from '@/shared/communicationAlerts/sendFast2smsMsg';
import { TriggerPoint } from '@/shared/constants/enum.constant';
import { sendWhatsappMessage } from '@/shared/communicationAlerts/sendMetaMsg';

const { OtpResponseCodes } = responseCodes;

async function dispatchOtpSms(
  phone: number,
  otpCode: number,
  name?: string,
  companyId?: string | Types.ObjectId,
): Promise<void> {
  await sendFast2smsMessage(
    companyId,
    TriggerPoint['OnLoginMobileApp'],
    String(phone),
    [name.toUpperCase(), String(otpCode)],
  );

  const whatsappVariables = [String(otpCode)];

  try {
    await sendWhatsappMessage({
      companyId: companyId as Types.ObjectId,
      triggerPoint: TriggerPoint['OnLoginMobileApp'],
      toNumber: String(phone),
      variables: whatsappVariables,
      isAuthenticationTemplate: true,
    });

    console.log('WhatsApp OTP sent successfully');
  } catch (error) {
    console.error('Failed to send WhatsApp OTP:', error);
    // Optionally handle error or fallback to SMS only scenario
  }
}

export const sendOtp = async (
  phone: number,
  name?: string,
  companyId?: string | Types.ObjectId,
): Promise<string> => {
  const orderId = uuidv4();
  const otpCode = generateFourDigitRandomNumber();

  try {
    await dispatchOtpSms(phone, otpCode, name, companyId);

    await Otp.deleteMany({ phone });
    await Otp.create({ phone, otp: otpCode, orderId });

    return orderId;
  } catch (_err) {
    console.log('🚀 ~ sendOtp ~ _err:', _err);

    throw new ApiError(
      defaultStatus.OK,
      'Failed to send OTP',
      true,
      '',
      OtpResponseCodes.FAILED_TO_SEND_OTP,
    );
  }
};

/**
 * Re-send the *same* OTP for an existing orderId.
 */
export const resendOtp = async (orderId: string): Promise<string> => {
  const doc = await Otp.findOne({ orderId });
  if (!doc)
    throw new ApiError(
      defaultStatus.OK,
      'OTP session expired',
      true,
      '',
      OtpResponseCodes.OTP_NOT_FOUND,
    );

  try {
    await Otp.deleteMany({ phone: doc.phone });

    const newOrderId = await sendOtp(doc.phone);
    return newOrderId;
  } catch (_err) {
    throw new ApiError(
      defaultStatus.OK,
      'Failed to resend OTP',
      true,
      '',
      OtpResponseCodes.FAILED_TO_RESEND_OTP,
    );
  }
};

export const verifyOtp = async (userBody: OtpPayload): Promise<boolean> => {
  try {
    const { otp, orderId }: OtpPayload = userBody;
    const otpRecord: OtpRecord | null = await Otp.findOne({
      orderId: orderId,
    });

    if (!otpRecord)
      throw new ApiError(
        defaultStatus.OK,
        'OTP expired or not found',
        true,
        '',
        OtpResponseCodes.OTP_EXPIRED,
      );

    if (otpRecord.otp !== otp)
      throw new ApiError(
        defaultStatus.OK,
        'Invalid OTP',
        true,
        '',
        OtpResponseCodes.INVALID_OTP,
      );

    await Otp.deleteOne({ _id: otpRecord._id });

    return true;
  } catch (error) {
    if (error instanceof ApiError) throw error;
    throw new ApiError(
      defaultStatus.OK,
      'Failed to verify OTP',
      true,
      '',
      OtpResponseCodes.INVALID_OTP,
    );
  }
};
