import jwt from "jsonwebtoken";
import svgCaptcha from "svg-captcha";
import { sejamServices, smsServices } from "$services/index.js";
import models from "$models/index.js";
import { appConfig, servicesConfig } from "$config/index.js";
import {
  authHelpers,
  clubHelpers,
  identityHelpers,
  sepahVerificationHelpers,
  tokenBlacklistHelpers,
} from "$helpers/index.js";
import { redisConnection } from "$connections/index.js";

export const ME = async (req, res) => {
  try {
    const user = req.user; // Set by JWT middleware

    if (!user) {
      throw { status: 401, message: "Unauthorized" };
    }

    const [userInfo, sejamRecord, sepahVerification] = await Promise.all([
      models.userInfo.findOne({ user: user._id }).lean(),
      models.sejam.findOne({ user: user._id }).select("_id").lean(),
      sepahVerificationHelpers.syncUserSepahVerification(user),
    ]);
    const enrichedUser = await authHelpers.buildUserWithPermissionDetails(
      sepahVerification?.user || user,
    );

    res.status(200).send({
      succeed: true,
      result: {
        ...enrichedUser,
        userInfo,
        hasSejamRecord: Boolean(sejamRecord?._id),
        isSepahVerified: Boolean(sepahVerification?.isSepahVerified),
        sepahRegisterUrl: servicesConfig.sepahBrokerConfig.registerUrl,
      },
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "ERROR FETCHING USER PROFILE";
    return res.status(status).send({ succeed: false, message });
  }
};

/**
 * Request OTP for national ID
 * POST /auth/sejam/request-otp
 * Body: { nationalId: "1234567890" }
 */
export const SEND_OTP = async (req, res) => {
  try {
    const { personType, identifier, captchaId, captchaAnswer } = req.body;
    const validation = identityHelpers.validateIdentifierByPersonType({
      personType,
      identifier,
    });

    if (!validation.isValid) {
      throw { status: 400, message: validation.message };
    }

    const resolvedIdentifier = validation.normalizedIdentifier;
    const identifierQuery = identityHelpers.getIdentifierValueByPersonType({
      personType,
      identifier: resolvedIdentifier,
    });

    authHelpers.ensureTestModeAccess(resolvedIdentifier);

    const hasCaptchaPayload =
      captchaId &&
      captchaAnswer !== undefined &&
      captchaAnswer !== null &&
      String(captchaAnswer).trim();

    if (hasCaptchaPayload) {
      await authHelpers.validateCaptcha(captchaId, captchaAnswer);
    } else {
      await authHelpers.validateOtpResendAccess(resolvedIdentifier);
    }

    const user = await models.user
      .findOne(
        validation.isPrivatePerson
          ? { nationalId: identifierQuery.nationalId }
          : { businessID: identifierQuery.businessID },
      )
      .populate("role");

    if (user) {
      if (!user.mobile) {
        throw { status: 400, message: "شماره موبایل کاربر ثبت نشده است" };
      }

      const otp = authHelpers.isDevelopmentUsers(user)
        ? authHelpers.DEV_USERS_OTP
        : authHelpers.generateOtp();

      await redisConnection.setex(
        authHelpers.getOtpKey(resolvedIdentifier),
        authHelpers.OTP_TTL_SECONDS,
        otp,
      );
      if (!authHelpers.isDevelopmentUsers(user)) {
        await smsServices.sendOTPSms({ mobile: user.mobile, message: otp });
      }
    } else if (
      authHelpers.shouldUseMockSejamFlow({
        identifier: resolvedIdentifier,
        userExists: false,
      })
    ) {
      await redisConnection.setex(
        authHelpers.getOtpKey(resolvedIdentifier),
        authHelpers.OTP_TTL_SECONDS,
        authHelpers.DEV_USERS_OTP,
      );
    } else {
      await sejamServices.requestOtp(resolvedIdentifier);
    }

    await authHelpers.grantOtpResendAccess(resolvedIdentifier);

    res.status(200).send({
      success: true,
      message: "کد تایید به شماره موبایل شما ارسال شد",
    });
  } catch (error) {
    console.error("❌ Error in requestOtp:", error);
    const status = error.status || 500;
    const message = error.message || "خطا در ارسال کد تایید";
    return res.status(status).send({ message });
  }
};

export const GET_CAPTCHA = async (_req, res) => {
  try {
    const challenge = svgCaptcha.create({
      size: 5,
      noise: 4,
      color: true,
      ignoreChars: "0Oo1IiLl",
      background: "#f7fafc",
    });
    const captchaId = authHelpers.generateCaptchaId();

    await redisConnection.setex(
      authHelpers.getCaptchaKey(captchaId),
      authHelpers.CAPTCHA_TTL_SECONDS,
      JSON.stringify({
        answer: challenge.text.toLowerCase(),
        attempts: 0,
      }),
    );

    res.status(200).send({
      success: true,
      result: {
        id: captchaId,
        svg: challenge.data,
      },
    });
  } catch (error) {
    console.error("❌ Error in getCaptcha:", error);
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت کپچا";
    return res.status(status).send({ message });
  }
};

/**
 * Verify OTP and login/register user
 * POST /auth/sejam/verify-otp
 * Body: { nationalId: "1234567890", otp: "12345" }
 */
export const VERIFY_OTP = async (req, res) => {
  try {
    const { personType, identifier, otp, referralCode } = req.body;
    const validation = identityHelpers.validateIdentifierByPersonType({
      personType,
      identifier,
    });

    // Validate inputs
    if (!validation.isValid || !otp) {
      throw {
        status: 400,
        message: validation.isValid
          ? "شناسه هویتی و کد تایید الزامی است"
          : validation.message,
      };
    }

    const resolvedIdentifier = validation.normalizedIdentifier;
    authHelpers.ensureTestModeAccess(resolvedIdentifier);

    // Validate OTP format (5 digits)
    if (!/^\d{5}$/.test(otp)) {
      throw { status: 400, message: "کد تایید باید 5 رقم باشد" };
    }

    let user = await models.user
      .findOne(
        validation.isPrivatePerson
          ? { nationalId: resolvedIdentifier }
          : { businessID: resolvedIdentifier },
      )
      .populate("role");

    if (user) {
      await authHelpers.validateUserOtp(resolvedIdentifier, otp);
      await redisConnection.del(authHelpers.getOtpKey(resolvedIdentifier));
      console.log(`✅ Existing user logged in: ${resolvedIdentifier}`);
    } else {
      let sejamProfile = null;

      if (
        authHelpers.shouldUseMockSejamFlow({
          identifier: resolvedIdentifier,
          userExists: false,
        })
      ) {
        await authHelpers.validateUserOtp(resolvedIdentifier, otp);
        await redisConnection.del(authHelpers.getOtpKey(resolvedIdentifier));
        sejamProfile = authHelpers.buildMockSejamProfile({
          identifier: resolvedIdentifier,
          isPrivatePerson: validation.isPrivatePerson,
        });
      } else {
        sejamProfile = await sejamServices.verifyOtpAndGetProfile(
          resolvedIdentifier,
          otp,
        );
      }

      user = await authHelpers.saveSejamUserAndInfo(
        resolvedIdentifier,
        sejamProfile,
      );
      await clubHelpers.handleNewUserClubRegistration({ user, referralCode });
      user = await models.user.findById(user._id).populate("role");
      console.log(`✅ New user registered: ${resolvedIdentifier}`);
    }

    // Generate JWT token
    const token = authHelpers.signAuthToken(user);

    if (!authHelpers.isDevelopmentUsers(user)) {
      try {
        await smsServices.sendLoginNotifSms({
          mobile: user.mobile,
          fullName: user.fullName,
        });
      } catch (error) {
        console.error("❌ Error in sendLoginNotifSms:", error);
      }
    }

    // Return token and user data
    res.status(200).send({
      token,
      user,
    });
  } catch (error) {
    console.error("❌ Error in verifyOtp:", error);
    const status = error.status || 500;
    const message = error.message || "خطا در تایید کد";
    return res.status(status).send({ message });
  }
};

export const IMPERSONATE_USER = async (req, res) => {
  try {
    const actor = req.user;
    const { userId } = req.params;

    if (!actor) {
      throw { status: 401, message: "Unauthorized" };
    }

    if (!actor.role?.isSuperAdmin && actor.role?.value !== "superAdmin") {
      throw {
        status: 403,
        message: "Only super admins can login as another user",
      };
    }

    const targetUser = await models.user.findById(userId).populate("role");

    if (!targetUser) {
      throw { status: 404, message: "کاربر یافت نشد" };
    }

    const token = authHelpers.signImpersonationLinkToken({
      actor,
      targetUser,
    });

    await redisConnection.setex(
      authHelpers.getImpersonationLinkKey(token),
      authHelpers.IMPERSONATION_LINK_TTL_SECONDS,
      targetUser._id.toString(),
    );

    res.status(200).send({
      message: "لینک ورود به حساب با موفقیت ایجاد شد",
      token,
      expiresIn: authHelpers.IMPERSONATION_LINK_TTL_SECONDS,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در ایجاد لینک ورود به حساب کاربر";
    return res.status(status).send({ message });
  }
};

export const CONSUME_IMPERSONATION_LINK = async (req, res) => {
  try {
    const { token } = req.body;

    if (!token) {
      throw { status: 400, message: "توکن لینک ورود الزامی است" };
    }

    const payload = jwt.verify(token, appConfig.secret);

    if (payload?.scope !== "impersonation-link" || !payload?.targetUserId) {
      throw { status: 400, message: "لینک ورود نامعتبر است" };
    }

    const storedTargetUserId = await authHelpers.consumeImpersonationLinkKey(
      token,
    );

    if (!storedTargetUserId) {
      throw {
        status: 401,
        message: "لینک ورود منقضی شده یا قبلا استفاده شده است",
      };
    }

    if (storedTargetUserId !== String(payload.targetUserId)) {
      throw { status: 400, message: "لینک ورود نامعتبر است" };
    }

    const targetUser = await models.user
      .findById(payload.targetUserId)
      .populate("role");

    if (!targetUser) {
      throw { status: 404, message: "کاربر یافت نشد" };
    }

    authHelpers.ensureTestModeAccess(
      identityHelpers.getUserIdentifier(targetUser),
    );

    const authToken = authHelpers.signAuthToken(targetUser);

    res.status(200).send({
      message: "ورود به حساب کاربر با موفقیت انجام شد",
      token: authToken,
      user: targetUser,
    });
  } catch (error) {
    const status =
      error.name === "TokenExpiredError" || error.name === "JsonWebTokenError"
        ? 401
        : error.status || 500;
    const message =
      error.name === "TokenExpiredError"
        ? "لینک ورود منقضی شده است"
        : error.name === "JsonWebTokenError"
          ? "لینک ورود نامعتبر است"
          : error.message || "خطا در استفاده از لینک ورود";
    return res.status(status).send({ message });
  }
};

export const SYNC_SEND_OTP = async (req, res) => {
  try {
    const identifier = req.identifier;

    if (!identifier) {
      throw { status: 400, message: "شناسه هویتی کاربر ثبت نشده است" };
    }

    await sejamServices.requestOtp(identifier);

    res.status(200).send({
      success: true,
      message: "کد تایید سجام به شماره موبایل ثبت‌شده شما ارسال شد",
    });
  } catch (error) {
    console.error("❌ Error in sync requestOtp:", error);
    const status = error.status || 500;
    const message = error.message || "خطا در ارسال کد تایید سجام";
    return res.status(status).send({ message });
  }
};

export const SYNC_VERIFY_OTP = async (req, res) => {
  try {
    const identifier = req.identifier;
    const { otp } = req.body;

    if (!identifier) {
      throw { status: 400, message: "شناسه هویتی کاربر ثبت نشده است" };
    }

    if (!otp) {
      throw { status: 400, message: "کد تایید الزامی است" };
    }

    if (!/^\d{5}$/.test(otp)) {
      throw { status: 400, message: "کد تایید باید 5 رقم باشد" };
    }

    const sejamProfile = await sejamServices.verifyOtpAndGetProfile(
      identifier,
      otp,
    );
    const user = await authHelpers.saveSejamUserAndInfo(identifier, sejamProfile, {
      preserveExistingRole: true,
    });
    const userInfo = await models.userInfo.findOne({ user: user._id }).lean();

    res.status(200).send({
      success: true,
      message: "اطلاعات سجام با موفقیت همگام‌سازی شد",
      user,
      userInfo,
    });
  } catch (error) {
    console.error("❌ Error in sync verifyOtp:", error);
    const status = error.status || 500;
    const message = error.message || "خطا در همگام‌سازی اطلاعات سجام";
    return res.status(status).send({ message });
  }
};

export const LOGOUT = async (req, res) => {
  try {
    const token = req.token; // Token stored by JWT middleware

    if (!token) {
      throw { status: 400, message: "No token provided" };
    }

    // Decode token to get expiration time
    const decoded = jwt.decode(token);
    const expiresIn = decoded.exp
      ? decoded.exp - Math.floor(Date.now() / 1000)
      : 86400;

    // Add token to blacklist
    await tokenBlacklistHelpers.revokeToken(
      token,
      expiresIn > 0 ? expiresIn : 0,
    );

    res.status(200).send({ message: "Logged out successfully" });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "ERROR ON LOGOUT";
    return res.status(status).send({ message });
  }
};
