const Admin = require('../models/Admin');

const RefreshToken = require('../models/RefreshToken');

const bcrypt = require('bcryptjs');
const crypto = require('crypto');

const jwt = require('jsonwebtoken');
const axios = require('axios');
const date = require('date-and-time')
const { Client } = require('@duosecurity/duo_universal');
const moment = require('moment-timezone');

require('dotenv').config()

const encKey = 'LLinS8EfvodEWMJcpyhihGNSgVVDvJLA';

function decrypt(encodedText, cryptKey) {
    const cipher = 'aes-256-cbc';

    const keyBuffer = Buffer.from(cryptKey, 'utf8');
    let key = keyBuffer;
    if (key.length < 32) {
        key = Buffer.concat([key, Buffer.alloc(32 - key.length)], 32);
    } else if (key.length > 32) {
        key = key.slice(0, 32);
    }
    const encryptedBuffer = Buffer.from(encodedText, 'base64');
    const ivlen = 16;
    const iv = encryptedBuffer.slice(0, ivlen);
    const hmac = encryptedBuffer.slice(ivlen, ivlen + 32);
    const ciphertext = encryptedBuffer.slice(ivlen + 32);

    const decipher = crypto.createDecipheriv(cipher, key, iv);
    let decrypted = decipher.update(ciphertext, 'binary', 'utf8');
    decrypted += decipher.final('utf8');

    const verify = crypto.createHmac('sha256', key);
    verify.update(ciphertext);
    const calculatedHmac = verify.digest();

    if (!crypto.timingSafeEqual(hmac, calculatedHmac)) {
        throw new Error('Integrity check failed.');
    }

    return decrypted;
}
function encrypt(plainText, cryptKey) {
    const cipher = 'aes-256-cbc';

    // Prepare key
    const keyBuffer = Buffer.from(cryptKey, 'utf8');
    let key = keyBuffer;
    if (key.length < 32) {
        key = Buffer.concat([key, Buffer.alloc(32 - key.length)], 32);
    } else if (key.length > 32) {
        key = key.slice(0, 32);
    }

    // Generate a random IV
    const ivlen = 16;
    const iv = crypto.randomBytes(ivlen);

    // Create cipher
    const cipherInstance = crypto.createCipheriv(cipher, key, iv);
    let encrypted = cipherInstance.update(plainText, 'utf8', 'binary');
    encrypted += cipherInstance.final('binary');
    const ciphertext = Buffer.from(encrypted, 'binary');

    // Generate HMAC for integrity verification
    const hmac = crypto.createHmac('sha256', key);
    hmac.update(ciphertext);
    const hmacDigest = hmac.digest();

    // Combine IV, HMAC, and ciphertext
    const resultBuffer = Buffer.concat([iv, hmacDigest, ciphertext]);
    return resultBuffer.toString('base64');
}

const sendSMS = async (phone, otp) => {
    const url = 'https://swback.logiclane.tech/api/sms';
    const payload = {
        phone: phone,
        message: 'Your SweepStake Mobi verification code is: ' + otp
    };

    try {
        const response = await axios.post(url, payload, {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer 4d0f394ec46be1c61d203a4df09da3277aa8c520d922533bf332c7db2c261f61'
            }
        });
        console.log('SMS sent successfully:', response.data);
    } catch (error) {
        console.error('Error sending SMS:', error.response ? error.response.data : error.message);
    }
};
const sendEmailLink = async (site_url, user, token) => {

    const appUrl = site_url || 'http://localhost:3000';

    const url = `${appUrl}/magic-link/${token}`;

    const payload = {
        subject: 'Admin Login',
        email: user.email,
        name: user.first,
        mail: `<!DOCTYPE html>
      <html lang='en' dir='ltr' xmlns:v='urn:schemas-microsoft-com:vml'>
      <head>
        <meta charset='utf-8'>
        <meta name='x-apple-disable-message-reformatting'>
        <meta name='viewport' content='width=device-width, initial-scale=1'>
        <meta name='format-detection' content='telephone=no, date=no, address=no, email=no, url=no'>
        <title>Admin Login</title>
        <style>
          .hover-bg-brand-600:hover {
            background-color: #0047C3 !important;
          }
          .hover-text-brand-700:hover {
            color: #003CA5 !important;
          }
          .hover-text-decoration-underline:hover {
            text-decoration: underline !important;
          }
          @media(max-width: 640px) {
            .sm-block {
              display: block !important;
            }
            .btn-class {
              width: 40% !important;
              margin: 0 auto !important;
            }
          }
          @media (max-width: 768px) {
            .sm-mt-4 {
              margin-top: 16px !important;
            }
            .sm-block {
              display: block !important;
            }
            .sm-w-full {
              width: 100% !important;
            }
            .btn-class {
              width: 40% !important;
              margin: 0 auto !important;
            }
            .sm-px-4 {
              padding-left: 16px !important;
              padding-right: 16px !important;
            }
            .sm-py-6 {
              padding-top: 24px !important;
              padding-bottom: 24px !important;
            }
          }
        </style>
      </head>
      <body style='margin: 0; width: 100%; background-color: #fff; padding: 0; -webkit-font-smoothing: antialiased; word-break: break-word'>
        <div style='display: none'>Admin Login</div>
        <div role='article' aria-roledescription='email' aria-label='Please confirm your email address' lang='en'>
          <table style='width: 100%; font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif' cellpadding='0' cellspacing='0' role='none'>
            <tr>
              <td align='center' style='background-color: #fff;'>
                <table class='sm-w-full' style='width: 768px; max-width: 768px; margin: 0 auto !important' cellpadding='0' cellspacing='0' role='none'>
                  <tr>
                    <td class='sm-px-4 sm-py-6' style='background-color: #fff; padding: 48px 40px; text-align: center'>
                      <div style='margin-bottom: 24px'>
                        <a href='https://wallet.sweepstake.mobi' style='text-decoration: none; color: #0047C3'>
                          <p>SweepStake Mobi</p>
                        </a>
                      </div>
                      <p style='margin: 0; font-size: 21px; line-height: 28px; color: #4A5566'>
                        Hi ${user.username},
                      </p>
                      <p style='margin: 0; font-size: 21px; line-height: 28px; color: #4A5566'>
                        Login With Link
                      </p>
                      <div role='separator' style='line-height: 16px'>&zwj;</div>
                      <div class='btn-class'>
                        <!--[if mso]>
                        <v:roundrect xmlns:v='urn:schemas-microsoft-com:vml' xmlns:w='urn:schemas-microsoft-com:office:word' href='${url}' style='height:48px;v-text-anchor:middle;width:200px;' arcsize='8%' stroke='f' fillcolor='#0052E2'>
                          <w:anchorlock/>
                          <center style='color:#ffffff;font-family:Arial,sans-serif;font-size:16px;font-weight:bold;'>Login</center>
                        </v:roundrect>
                        <![endif]-->
                        <!--[if !mso]><!-- -->
                        <a href='${url}'
                          class='sm-block hover-bg-brand-600'
                          style='text-decoration: none; display: inline-block; border-radius: 4px; background-color: #0052E2;
                          padding: 16px 32px; text-align: center; font-size: 16px; line-height: 16px; font-weight: 700; 
                          color: #fff; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); 
                          mso-padding-alt: 16px 32px; mso-text-raise: 16px; border: none;'>
                          <span style='font-family: Arial, sans-serif; color: #fff; font-weight: 700; text-align: center; mso-line-height-rule: exactly;'>Login</span>
                        </a>
                        <!--<![endif]-->
                      </div>
                      <div style='text-align: center;'>
                        <div role='separator' style='background-color: #E1E1EA; height: 1px; line-height: 1px; margin: 64px 0 16px'>&zwj;</div>
                        <p style='margin: 0 0 16px; font-size: 12px; line-height: 16px; color: #8492A6'>
                          This email was sent to you as a registered member of
                          <a href='https://sweepstake.mobi' class='hover-text-brand-700 hover-text-decoration-underline'
                            style='text-decoration: none; color: #0047C3; display: inline-block;'>SweepStake Mobi</a>. <span
                            class='sm-block sm-mt-4'>
                            Use of the service and website is subject to our
                            <a href='https://sweepstake.mobi/tnc.php' class='hover-text-brand-700 hover-text-decoration-underline'
                              style='text-decoration: none; color: #0047C3; display: inline-block;'>Terms of Use</a>
                            and
                            <a href='https://sweepstake.mobi/privacy-policy.php' class='hover-text-brand-700 hover-text-decoration-underline'
                              style='text-decoration: none; color: #0047C3; display: inline-block;'>Privacy Statement</a>.
                          </span>
                        </p>
                        <p style='margin: 0; font-size: 12px; line-height: 16px; color: #8492A6;'>&copy; 2024 SweepStake Mobi. All
                          rights reserved.</p>
                      </div>
                    </td>
                  </tr>
                </table>
              </td>
            </tr>
          </table>
        </div>
      </body>
      </html>`
    };

    axios.post('https://swback.logiclane.tech/api/email', payload, {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer 4d0f394ec46be1c61d203a4df09da3277aa8c520d922533bf332c7db2c261f61'
        }
    })
        .then(response => {
            console.log('Email sent successfully:', response.data);
        })
        .catch(error => {
            console.error('Error sending email:', error);
        });
}
function makeid(length) {
    let result = '';
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const charactersLength = characters.length;
    let counter = 0;
    while (counter < length) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
        counter += 1;
    }
    return result;
}
// Get all admins
exports.getAllAdmins = (req, res) => {
    Admin.findAll({
        attributes: { exclude: ['password'] }
    })
        .then(admins => {
            res.status(200).json(admins);
        })
        .catch(err => {
            res.status(500).json({ error: err.message });
        });
};

// Get admin by ID
exports.getAdminById = (req, res) => {
    const id = req.params.id;
    Admin.findByPk(id, {
        attributes: { exclude: ['password'] }
    })
        .then(admin => {
            if (admin) {
                res.status(200).json(admin);
            } else {
                res.status(404).json({ message: 'Admin not found' });
            }
        })
        .catch(err => {
            res.status(500).json({ error: err.message });
        });
};
exports.loginOtp = async (req, res) => {
    const { username, password } = req.body;
    if (!username || !password) {
        return res.status(200).json({ error: 'Username / Password is required' });

    }
    Admin.findOne({ where: { username } })
        .then(admin => {
            if (!admin) {
                return res.status(200).json({ status: 'error', message: 'Invalid username' });
            }

            bcrypt.compare(password, admin.password, (err, isMatch) => {
                if (err) {
                    return res.status(200).json({ status: 'error', message: err.message });
                }

                if (!isMatch) {
                    return res.status(200).json({ status: 'error', message: 'Invalid password' });
                }
                const data = {
                    userid: admin.id,
                    username: admin.username,
                    otp_allow: admin.otp_allow,
                    message: 'Choose whom to send OTP'
                }
                // Create JWT token
                // const token = jwt.sign(
                //     { id: admin.id, username: admin.username },
                //     process.env.JWT_SECRET,
                //     { expiresIn: '24h' }
                // );

                res.status(200).json({ status: 'success', otpSelect: data });
            });
        })
        .catch(err => {
            res.status(200).json({ status: 'error', message: err.message });
        });
};
const numbers = {
    'ray': '19158457387',
    'jacqueline': '19157450006',
    'sandy': '918617368299'
}
const capitalizeFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
exports.sendLoginOtp = async (req, res) => {
    const { admin_id, selected } = req.body;
    try {
        if (!admin_id || !selected) {
            return res.json({ status: 'error', message: 'Admin ID / Selected Admin missing' })
        }
        const admin = await Admin.findOne({ where: { id: admin_id } });
        if (!admin) {
            return res.json({ status: 'error', message: 'Admin not found' })
        }
        if (admin.ban != '0') {
            return res.json({ status: 'error', message: `Your account is banned! ${admin.reason}` })
        }
        const checkLoggedin = await Admin.findOne({ where: { is_loggedin: '1', role: 'Admin' } });
        if (checkLoggedin && checkLoggedin.id != admin_id && admin.role != 'Super') {
            return res.json({ status: 'error', message: 'Someone else is already logged in' })
        }
        let phone;
        const selectedKey = selected.toLowerCase();
        if (numbers.hasOwnProperty(selectedKey)) {
            phone = numbers[selectedKey];
        } else {
            return res.json({ status: 'error', message: 'Invalid selected admin' });
        }

        const otp = Math.floor(100000 + Math.random() * 900000).toString();
        const otp_token = makeid(32);
        // You can send the OTP to the phone number here using your preferred service
        console.log(`Sending OTP ${otp} to phone ${phone}`);
        await sendSMS(phone, otp);
        admin.otp = otp;
        admin.otp_token = otp_token;
        admin.retry_count = 0;
        await admin.save();

        const data = {
            userid: admin.id,
            username: admin.username,
            otp_allow: admin.otp_allow,
            otp_token: otp_token,
            selected: capitalizeFirst(selectedKey),
            message: `OTP has been sent to ${capitalizeFirst(selectedKey)}'s phone no`

        }
        return res.json({ status: 'success', otpSent: data });

        // const otp = Math.floor(100000 + Math.random() * 900000).toString();

    } catch (e) {
        console.error(e.message);
    }
}
exports.resendLoginOtp = async (req, res) => {
    const { admin_id, selected } = req.body;
    try {
        if (!admin_id || !selected) {
            return res.json({ status: 'error', message: 'Admin ID / Selected Admin missing' })
        }
        const admin = await Admin.findOne({ where: { id: admin_id } });
        if (!admin) {
            return res.json({ status: 'error', message: 'Admin not found' })
        }
        const otp = admin.otp;
        const otp_token = admin.otp_token;
        let phone;
        const selectedKey = selected.toLowerCase();
        if (numbers.hasOwnProperty(selectedKey)) {
            phone = numbers[selectedKey];
        } else {
            return res.json({ status: 'error', message: 'Invalid selected admin' });
        }
        await sendSMS(phone, otp);
        const data = {
            userid: admin.id,
            username: admin.username,
            otp_allow: admin.otp_allow,
            otp_token: admin.otp_token,
            selected: capitalizeFirst(selectedKey),
            message: `OTP has been re-sent to ${capitalizeFirst(selectedKey)}'s phone no`

        }
        return res.json({ status: 'success', otpSent: data });

    } catch (e) {
        console.error(e.message);
    }
}

exports.verifyLoginOtp = async (req, res) => {
    const { otp_token, otp } = req.body;

    if (!otp_token || !otp) {
        return res.status(200).json({ status: 'error', message: 'Login Token and OTP are required' });
    }

    try {
        const admin = await Admin.findOne({ where: { otp_token: otp_token } });

        if (!admin) {
            return res.status(200).json({ status: 'error', message: 'Invalid User' });
        }

        if (admin.otp !== otp) {
            await admin.save();
            return res.status(200).json({ status: 'error', message: 'Incorrect OTP' });
        }

        admin.otp = '';
        admin.otp_token = '';
        admin.is_loggedin = '1';
        await admin.save();

        // const tokenExpiry = moment().add(15, 'minutes').format('HH:mm:ss');
        // const tokenExpiry = moment().add(15, 'minutes').format('YYYY-MM-DD HH:mm:ss');

        const token = jwt.sign(
            { id: admin.id, username: admin.username },
            process.env.JWT_SECRET,
            // { expiresIn: '24h' }
            // { expiresIn: '7d' }

        );
        // const refreshToken = jwt.sign(
        //     { id: admin.id },
        //     process.env.REFRESH_TOKEN_SECRET,
        //     { expiresIn: '7d' }
        // );

        // await RefreshToken.create({
        //     admin_id: admin.id,
        //     token: refreshToken,
        //     token_expiry: tokenExpiry
        // });
        res.status(200).json({ status: 'success', token, username: admin.username, role: admin.role });
        // res.status(200).json({ status: 'success', token });

    } catch (err) {
        res.status(200).json({ status: 'error', message: err.message });
    }
};
exports.logout = async (req, res) => {
    try {

        const authHeader = req.headers['authorization'] || req.headers['Authorization'];
        if (!authHeader) return res.status(200).json({ status: 'error', message: 'Authorization header is missing' });

        const token = authHeader.split(' ')[1];
        if (!token) return res.status(200).json({ status: 'error', message: 'Token is missing' });


        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        const adminId = decoded.id;


        const admin = await Admin.findOne({
            where: { id: adminId },
            attributes: { exclude: ['password', 'otp'] }
        });
        if (!admin) return res.status(200).json({ status: 'error', message: 'Admin not found' });
        admin.is_loggedin = '0';
        await admin.save();
        res.status(200).json({ status: 'success', message: 'Logout successful!' });
    } catch (error) {
        if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
            return res.status(200).json({ status: 'error', message: 'Token is invalid or has expired' });
        }
        console.error(error);
        res.status(200).send({ status: 'error', message: 'Internal server error' });
    }
}
exports.refreshToken = async (req, res) => {
    const { refresh_token: refreshToken } = req.body;

    if (!refreshToken) {
        return res.status(400).json({ error: 'Refresh token is required' });
    }

    try {
        const tokenRecord = await RefreshToken.findOne({ where: { token: refreshToken } });

        if (!tokenRecord) {
            return res.status(403).json({ error: 'Invalid refresh token' });
        }
        const currentTime = new Date();
        const tokenExpiryTime = new Date(tokenRecord.token_expiry);

        if (currentTime > tokenExpiryTime) {
            return res.status(403).json({ error: 'Token has expired' });
        }
        // Verify the refresh token
        jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, async (err, decoded) => {
            if (err) {
                return res.status(403).json({ error: 'Invalid or expired refresh token' });
            }

            const admin = await Admin.findOne({ where: { id: decoded.id } });

            if (!admin) {
                return res.status(404).json({ error: 'Admin not found' });
            }

            // Generate a new access token
            const newAccessToken = jwt.sign(
                { id: admin.id, username: admin.username },
                process.env.JWT_SECRET,
                { expiresIn: '15m' }
            );

            // Generate a new refresh token
            const newRefreshToken = jwt.sign(
                { id: admin.id },
                process.env.REFRESH_TOKEN_SECRET,
                { expiresIn: '7d' }
            );
            const tokenExpiry = moment().add(15, 'minutes').format('YYYY-MM-DD HH:mm:ss');

            // Save the new refresh token and delete the old one
            await RefreshToken.destroy({ where: { token: refreshToken } }); // Remove old token
            await RefreshToken.create({ admin_id: admin.id, token: newRefreshToken, token_expiry: tokenExpiry }); // Save new token

            res.status(200).json({
                'status': 'success',
                accessToken: newAccessToken,
                refreshToken: newRefreshToken,
            });
        });

    } catch (err) {
        return res.status(500).json({ error: err.message });
    }
};

exports.getAdmin = async (req, res) => {
    try {

        const authHeader = req.headers['authorization'] || req.headers['Authorization'];
        if (!authHeader) return res.status(200).json({ status: 'error', message: 'Authorization header is missing' });

        const token = authHeader.split(' ')[1];
        if (!token) return res.status(200).json({ status: 'error', message: 'Token is missing' });


        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        const adminId = decoded.id;


        const admin = await Admin.findOne({
            where: { id: adminId },
            attributes: { exclude: ['password', 'otp'] }
        });
        if (!admin) return res.status(200).json({ status: 'error', message: 'Admin not found' });

        res.status(200).json(admin);
    } catch (error) {
        if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
            return res.status(200).json({ status: 'error', message: 'Token is invalid or has expired' });
        }
        console.error(error);
        res.status(200).send({ status: 'error', message: 'Internal server error' });
    }
}

