/**
 * Auth Service
 * Business logic for authentication operations
 */

const User = require('../models/user.model');
const Driver = require('../models/driver.model');
const ZoneAdmin = require('../models/zone-admin.model');
const Staff = require('../models/staff.model');
const findUserInAllCollections = require('../helper/FindUserInAllCollections');

// ==========================================
// HELPER FUNCTIONS
// ==========================================

/**
 * Find user by ID and role
 * @param {String} id - User ID
 * @param {String} role - User role
 */
exports.findUserByIdAndRole = async (id, role) => {
    if (role === 'driver') return Driver.findById(id);
    if (role === 'zone-admin') return ZoneAdmin.findById(id);
    if (role === 'staff') return Staff.findById(id);
    return User.findById(id);
};

/**
 * Find user across all collections by email
 * @param {String} email - User email
 */
exports.findUserAcrossCollections = async (email) => {
    return findUserInAllCollections(email);
};

// ==========================================
// AUTH SERVICES
// ==========================================

/**
 * Register a new user
 * @param {Object} userData - User registration data
 * @param {String} profileImageUrl - Optional profile image URL
 */
exports.registerUser = async (userData, profileImageUrl = null) => {
    const { fullName, email, phone, password } = userData;

    // Check if user already exists
    const { user: existingUser } = await findUserInAllCollections(email);
    if (existingUser) {
        throw { message: 'Email already exists', statusCode: 400 };
    }

    // Create user
    const user = await User.create({
        fullName,
        email,
        phone,
        password,
        profileImageUrl,
        role: 'user',
        status: 'Active'
    });

    user.password = undefined;
    return user;
};

/**
 * Authenticate user with email and password
 * @param {String} email - User email
 * @param {String} password - User password
 */
exports.authenticateWithPassword = async (email, password) => {
    const { user, collection } = await findUserInAllCollections(email);

    if (!user) {
        throw { message: 'Invalid email or password', statusCode: 401 };
    }

    const isPasswordMatch = await user.comparePassword(password);
    if (!isPasswordMatch) {
        throw { message: 'Invalid email or password', statusCode: 401 };
    }

    // Check if user is active
    if (user.status === 'Suspended' || user.status === 'Banned') {
        throw { message: 'Your account has been deactivated. Please contact support.', statusCode: 403 };
    }

    return { user, collection };
};

/**
 * Authenticate user with phone OTP
 * @param {String} phone - User phone number
 * @param {String} otp - OTP code
 * @param {Function} verifyOtpFn - Function to verify OTP
 */
exports.authenticateWithOtp = async (phone, otp, verifyOtpFn) => {
    const result = await verifyOtpFn(phone, otp, 'phone');

    if (!result.verified) {
        throw { message: 'Invalid OTP', statusCode: 401 };
    }

    const user = await User.findOne({ phone });
    if (!user) {
        throw { message: 'User not found', statusCode: 404 };
    }

    if (user.status === 'Suspended' || user.status === 'Banned') {
        throw { message: 'Your account has been deactivated. Please contact support.', statusCode: 403 };
    }

    return user;
};

/**
 * Refresh access token
 * @param {String} refreshToken - Refresh token
 * @param {Object} decoded - Decoded token payload
 */
exports.refreshTokens = async (refreshToken, decoded) => {
    const user = await exports.findUserByIdAndRole(decoded.id, decoded.role);

    if (!user) {
        throw { message: 'User not found', statusCode: 404 };
    }

    // For User model, validate refresh token exists in array
    if (decoded.role !== 'driver' && decoded.role !== 'zone-admin') {
        if (!user.refreshToken || !user.refreshToken.includes(refreshToken)) {
            throw { message: 'Refresh token not found or revoked. Please login again', statusCode: 401 };
        }
    }

    return user;
};

/**
 * Logout user and invalidate tokens
 * @param {String} userId - User ID
 * @param {String} role - User role
 * @param {String} refreshToken - Token to invalidate
 */
exports.logoutUser = async (userId, role, refreshToken) => {
    if (role === 'driver') {
        // Driver doesn't have refresh token array logic yet
        return;
    }

    if (role === 'zone-admin') {
        // Zone admin doesn't have refresh token array logic yet
        return;
    }

    if (role === 'staff') {
        await Staff.findByIdAndUpdate(userId, {
            $pull: { refreshToken: refreshToken }
        });
        return;
    }

    // For regular users, remove the refresh token from array
    await User.findByIdAndUpdate(userId, {
        $pull: { refreshToken: refreshToken }
    });
};

/**
 * Initiate password reset
 * @param {String} email - User email
 * @param {String} resetToken - Password reset token
 */
exports.initiatePasswordReset = async (email, resetToken) => {
    const { user } = await findUserInAllCollections(email);

    if (!user) {
        throw { message: 'User not found', statusCode: 404 };
    }

    user.resetToken = resetToken;
    user.resetTokenExpiry = Date.now() + 10 * 60 * 1000; // 10 minutes
    await user.save();

    return user;
};

/**
 * Reset password using token
 * @param {String} resetToken - Password reset token
 * @param {String} newPassword - New password
 */
exports.resetPassword = async (resetToken, newPassword) => {
    // Search for user with valid reset token
    let user = await User.findOne({
        resetToken,
        resetTokenExpiry: { $gt: Date.now() }
    });

    if (!user) {
        user = await Driver.findOne({
            resetToken,
            resetTokenExpiry: { $gt: Date.now() }
        });
    }

    if (!user) {
        user = await ZoneAdmin.findOne({
            resetToken,
            resetTokenExpiry: { $gt: Date.now() }
        });
    }

    if (!user) {
        // Correctly require Vendor model
        const Vendor = require('../models/vendor.model');
        user = await Vendor.findOne({
            resetToken,
            resetTokenExpiry: { $gt: Date.now() }
        });
    }

    if (!user) {
        throw { message: 'Invalid or expired reset token', statusCode: 400 };
    }

    user.password = newPassword;
    user.resetToken = undefined;
    user.resetTokenExpiry = undefined;
    await user.save();

    user.password = undefined;
    user.refreshToken = undefined;

    return user;
};

/**
 * Update user profile
 * @param {String} userId - User ID
 * @param {String} role - User role
 * @param {Object} updates - Profile updates
 */
exports.updateProfile = async (userId, role, updates) => {
    const user = await exports.findUserByIdAndRole(userId, role);

    if (!user) {
        throw { message: 'User not found', statusCode: 404 };
    }

    const { fullName, email, phone, profileImageUrl, fullAddress, gender, dob, nationality, maritalStatus, anniversaryDate } = updates;

    if (fullName) user.fullName = fullName;
    if (phone) user.phone = phone;
    if (profileImageUrl) user.profileImageUrl = profileImageUrl;

    // Email update logic
    if (email && email !== user.email) {
        // Check if new email is already taken
        const { user: existingUser } = await findUserInAllCollections(email);
        if (existingUser) {
            throw { message: 'Email is already in use by another account', statusCode: 400 };
        }
        user.email = email;
    }

    if (fullAddress) {
        user.fullAddress = {
            ...user.fullAddress, // Keep existing if strictly necessary, but actually we probably want to overwrite if provided
            ...fullAddress
        };
    }

    if (gender) user.gender = gender;
    if (dob) user.dob = dob;
    if (nationality) user.nationality = nationality;
    if (maritalStatus) user.maritalStatus = maritalStatus;
    if (anniversaryDate) user.anniversaryDate = anniversaryDate;

    // Check if profile is now complete (has real email and address)
    if (user.email && !user.email.startsWith('user_') && user.fullAddress?.address) {
        user.isProfileComplete = true;
    }

    await user.save();

    user.password = undefined;
    user.refreshToken = undefined;

    return user;
};

/**
 * Change user password
 * @param {String} userId - User ID
 * @param {String} role - User role
 * @param {String} currentPassword - Current password
 * @param {String} newPassword - New password
 */
exports.changePassword = async (userId, role, currentPassword, newPassword) => {
    const user = await exports.findUserByIdAndRole(userId, role);

    if (!user) {
        throw { message: 'User not found', statusCode: 404 };
    }

    // Get user with password field
    let userWithPass;
    if (role === 'driver') {
        userWithPass = await Driver.findById(userId).select('+password');
    } else if (role === 'zone-admin') {
        userWithPass = await ZoneAdmin.findById(userId).select('+password');
    } else if (role === 'staff') {
        userWithPass = await Staff.findById(userId).select('+password');
    } else {
        userWithPass = await User.findById(userId).select('+password');
    }

    const isMatch = await userWithPass.comparePassword(currentPassword);
    if (!isMatch) {
        throw { message: 'Current password is incorrect', statusCode: 400 };
    }

    userWithPass.password = newPassword;
    await userWithPass.save();

    return true;
};
