const { Admin, sequelize, AdminOTP } = require('../../models')
const storage = require('../../config/context');
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');
const config = require('../../config');
const { UnauthorizedError, BadRequestError } = require('../../utils/errors');
const logger = require('../../utils/logger');
const otpService = require('./otpService');
const { generateRandomString } = require('../../utils/helpers');


class profileService {
    sanitizeAdmin(admin) {
        if (!admin) return null;

        const plain = typeof admin.toJSON === 'function' ? admin.toJSON() : { ...admin };

        delete plain.password;
        delete plain.pin;
        delete plain.twoFactorSecret;
        delete plain.backupCodes;
        delete plain.emailVerificationToken;
        delete plain.passwordResetToken;
        delete plain.passwordResetExpires;

        const avatar = plain.avatar || plain.profileImage || null;

        return {
            ...plain,
            avatar,
            profileImage: avatar,
        };
    }

    async getAdminProfile(adminId, query) {
        const admin = await Admin.findByPk(adminId);

        if (!admin) {
            throw new NotFoundError('Admin not found');
        }

        const safeAdmin = this.sanitizeAdmin(admin);

        const fullName =
            `${safeAdmin.firstName || ''} ${safeAdmin.lastName || ''}`.trim() || 'Admin';

        const initials =
            `${safeAdmin.firstName?.[0] || ''}${safeAdmin.lastName?.[0] || ''}`.toUpperCase() || 'U';

        return {
            profile: {
                id: safeAdmin.id,
                traderId: safeAdmin.traderId || null,
                fullName,
                initials,
                firstName: safeAdmin.firstName || '',
                lastName: safeAdmin.lastName || '',
                adminname: safeAdmin.adminname || '',
                role: safeAdmin.role || 'Admin',
                email: safeAdmin.email || '',
                phone: safeAdmin.phone || '',
                country: safeAdmin.country || '',
                city: safeAdmin.city || '',
                currency: safeAdmin.currency || 'USD',
                avatar: safeAdmin.avatar || null,
                profileImage: safeAdmin.profileImage || null,
                createdAt: safeAdmin.createdAt || null,
                joinDate: safeAdmin.createdAt || null,
                accountType: safeAdmin.accountType || 'Premium',
                lastLogin: safeAdmin.lastLogin || null,
            },
            accountSnapshot: {
                fullName,
                adminname: safeAdmin.adminname || '',
                currency: safeAdmin.currency || 'USD',
                country: safeAdmin.country || '',
                city: safeAdmin.city || '',
            },
            security: {
                twoFactorEnabled: !!safeAdmin.twoFactorEnabled,
                twoFactorMethod: safeAdmin.twoFactorMethod

            }

        };
    }

    async changePassword(adminId, currentPassword, newPassword) {
        const admin = await Admin.findByPk(adminId);

        if (!admin) {
            throw new NotFoundError('Admin not found');
        }

        const isMatch = await admin.comparePassword(currentPassword);
        if (!isMatch) {
            throw new UnauthorizedError('Current password is incorrect');
        }

        await admin.update({
            password: newPassword,
            lastPasswordChange: new Date(),
        });

        logger.info(`Password changed: ${admin.email}`);

        return {
            lastPasswordChange: 'Just now',
        };
    }

    async verifyPassword2fa(adminId, data, ip, device) {
        const { password, method: type } = data;
        console.log("2fa data: ", data);
        if (!['email', 'authenticator'].includes(type)) {
            throw new BadRequestError('Invalid 2FA type');
        }

        const admin = await Admin.findByPk(adminId);
        if (!admin) {
            throw new NotFoundError('Admin not found');
        }

        const isMatch = await admin.comparePassword(password);
        if (!isMatch) {
            throw new UnauthorizedError('Current password is incorrect');
        }

        // =========================
        // IF 2FA NOT ENABLED (SETUP)
        // =========================
        if (!admin.twoFactorEnabled) {

            // 🔐 AUTHENTICATOR SETUP
            if (type === 'authenticator') {

                const secret = speakeasy.generateSecret({
                    name: `${config.twoFA.appName}:${admin.email}`,
                    length: 32,
                });

                const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);

                await admin.update({
                    twoFactorTempSecret: secret.base32, // TEMP storage
                    twoFactorMethod: 'authenticator',
                });

                return {
                    qrCodeUrl,
                    manualEntryCode: secret.base32,
                    method: 'authenticator',
                    message: 'Scan QR and verify code to activate'
                };
            }

            // 📧 EMAIL OTP SETUP
            if (type === 'email') {
                await otpService.generateOTP(adminId, '2fa', ip, device);

                return {
                    method: 'email',
                    message: 'OTP sent to your email'
                };
            }
        }

        // =========================
        // IF 2FA ALREADY ENABLED
        // =========================
        return {
            message: '2FA already enabled. Use verify endpoint.',
        };
    }


    async enable2FA(adminId, data) {
        const { code, method } = data;

        const transaction = await sequelize.transaction();

        try {
            const admin = await Admin.findByPk(adminId, { transaction });

            if (!admin) {
                throw new NotFoundError('Admin not found');
            }

            const backupCodes = Array.from({ length: 10 }, () =>
                generateRandomString(8).toUpperCase()
            );

            // =========================
            // AUTHENTICATOR FLOW
            // =========================
            if (method === 'authenticator') {

                if (!admin.twoFactorTempSecret) {
                    throw new BadRequestError('Please setup 2FA first');
                }

                const isValid = speakeasy.totp.verify({
                    secret: admin.twoFactorTempSecret,
                    encoding: 'base32',
                    token: code,
                    window: 1,
                });

                if (!isValid) {
                    throw new BadRequestError('Invalid 2FA code');
                }

                await admin.update({
                    twoFactorEnabled: true,
                    twoFactorSecret: admin.twoFactorTempSecret,
                    twoFactorTempSecret: null, // ✅ VERY IMPORTANT
                    twoFactorMethod: 'authenticator',
                    backupCodes,
                }, { transaction });

                await transaction.commit();

                logger.info(`2FA enabled (authenticator): ${admin.email}`);

                return {
                    twoFactorEnabled: true,
                    twoFactorMethod: 'authenticator',
                    backupCodes,
                };
            }

            // =========================
            // EMAIL OTP FLOW
            // =========================
            if (method === 'email') {

                const otpres = await otpService.verifyOTP(adminId, '2fa', code);

                if (!otpres?.status) {
                    throw new BadRequestError('Invalid 2FA code');
                }

                await admin.update({
                    twoFactorEnabled: true,
                    twoFactorMethod: 'email',
                    backupCodes,
                }, { transaction });

                await transaction.commit();

                logger.info(`2FA enabled (email): ${admin.email}`);

                return {
                    twoFactorEnabled: true,
                    twoFactorMethod: 'email', // ✅ FIXED
                    backupCodes,
                };
            }

            throw new BadRequestError('Invalid 2FA method');

        } catch (error) {
            await transaction.rollback();

            logger.error(`Enable 2FA failed: ${error.message}`);

            throw error;
        }
    }


    async disable2FA(adminId, data) {
        const { password, code } = data
        const admin = await Admin.findByPk(adminId);

        if (!admin) {
            throw new NotFoundError('Admin not found');
        }

        if (!admin.twoFactorEnabled) {
            throw new BadRequestError('2FA is not enabled');
        }
        
         const isMatch = await admin.comparePassword(password);
        if (!isMatch) {
            throw new UnauthorizedError('Current password is incorrect');
        }

        
        if (admin.twoFactorMethod === 'authenticator') {

            if (!admin.twoFactorSecret) {
                throw new BadRequestError('Please setup 2FA first');
            }

            const isValid = speakeasy.totp.verify({
                secret: admin.twoFactorSecret,
                encoding: 'base32',
                token: code,
                window: 1,
            });

            if (!isValid) {
                throw new BadRequestError('Invalid 2FA code');
            }

        }

       
        // =========================
        // EMAIL OTP FLOW
        // =========================
        if (admin.twoFactorMethod === 'email') {

            const otpres = await otpService.verifyOTP(adminId, '2fa', code);

            if (!otpres?.status) {
                throw new BadRequestError('Invalid 2FA code');
            }
        }

        await admin.update({
            twoFactorEnabled: false,
            twoFactorMethod: null,
            twoFactorSecret: null,
            backupCodes: [],
        });

        logger.info(`2FA disabled: ${admin.email}`);

        return {
            twoFactorEnabled: false,
            twoFactorMethod: null,
        };
    }

    async resend2fa(adminId, ip, device) {


        const otp = await AdminOTP.findOne({
            where: {
                adminId,
                type: '2fa',
            },
            order: [['createdAt', 'DESC']],
        });

        if (otp) {
            const now = new Date();
            const diffSeconds = (now - new Date(otp.createdAt)) / 1000;

            if (diffSeconds < 60) {
                throw new BadRequestError('Please wait 60 seconds before resending OTP');
            }
        }

        await AdminOTP.invalidatePreviousOTPs(adminId, '2fa')

        const token = await otpService.generateOTP(adminId, '2fa', ip, device);

        return { message: "Email otp send" };
    }

    async regenerateBackupCodes(adminId) {
        const admin = await Admin.findByPk(adminId);

        if (!admin) {
            throw new NotFoundError('Admin not found');
        }

        if (!admin.twoFactorEnabled) {
            throw new BadRequestError('Enable 2FA before generating backup codes');
        }

        const backupCodes = Array.from({ length: 10 }, () => generateRandomString(8).toUpperCase());

        await admin.update({ backupCodes });

        return { backupCodes };
    }

    async revokeSession(adminId, sessionId) {
        const session = await Session.findOne({
            where: { id: sessionId, adminId },
        });

        if (!session) {
            throw new NotFoundError('Session not found');
        }

        await session.update({
            isActive: false,
            revokedAt: new Date(),
        });

        return true;
    }

    async revokeAllSessions(adminId, currentSessionId = null) {
        const where = {
            adminId,
            isActive: true,
        };

        if (currentSessionId) {
            where.id = { [Op.ne]: currentSessionId };
        }

        await Session.update(
            {
                isActive: false,
                revokedAt: new Date(),
            },
            { where }
        );

        return true;
    }

}

module.exports = new profileService();
