const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const config = require('../config/config');
const { User } = require('../models');
const { createInAppNotification } = require('../services/PushNotificationService');

class AuthController {
  async login(req, res, next) {
    try {
      const { email, password } = req.body;

      const user = await User.findOne({ where: { email } });
      if (!user) {
        return res.status(401).json({ error: 'Invalid email or password' });
      }

      const validPassword = await bcrypt.compare(password, user.password_hash);
      if (!validPassword) {
        return res.status(401).json({ error: 'Invalid email or password' });
      }

      if (!user.is_active) {
        return res.status(403).json({ error: 'Account is deactivated', block_reason: user.block_reason });
      }

      user.last_login = new Date();
      await user.save();

      const deviceInfo = req.headers['user-agent'] || 'Unknown device';
      const ip = req.ip || req.connection?.remoteAddress || 'Unknown';
      const location = req.body.location || 'Unknown';
      await createInAppNotification(
        user.email, user.id, 'login',
        'New Login Detected',
        `New sign-in from ${deviceInfo.split('/')[0] || deviceInfo} at ${new Date().toLocaleString()}. IP: ${ip}${location !== 'Unknown' ? `. Location: ${location}` : ''}`,
        { type: 'login' }
      );

      const token = jwt.sign(
        { id: user.id, role: user.role, email: user.email },
        config.jwt.secret,
        { expiresIn: config.jwt.expiresIn }
      );

      res.json({
        token,
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          full_name: user.full_name,
          role: user.role,
          phone: user.phone,
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async register(req, res, next) {
    try {
      const { username, email, password, full_name, phone, role } = req.body;

      const existing = await User.findOne({ where: { email } });
      if (existing) {
        return res.status(409).json({ error: 'Email already registered' });
      }

      const salt = await bcrypt.genSalt(10);
      const password_hash = await bcrypt.hash(password, salt);

      const user = await User.create({
        username,
        email,
        password_hash,
        full_name,
        phone,
        role: role || 'staff',
      });

      res.status(201).json({
        message: 'User created successfully',
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          full_name: user.full_name,
          role: user.role,
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async me(req, res) {
    const user = await User.findByPk(req.user.id, {
      attributes: { exclude: ['password_hash', 'reset_password_token', 'reset_password_expires'] },
    });
    res.json({ user });
  }

  async updateProfile(req, res, next) {
    try {
      const { full_name, phone, username } = req.body;
      const updates = {};
      if (full_name !== undefined) updates.full_name = full_name;
      if (phone !== undefined) updates.phone = phone;
      if (username !== undefined) updates.username = username;

      const user = await User.findByPk(req.user.id);
      if (!user) return res.status(404).json({ error: 'User not found' });

      if (username && username !== user.username) {
        const existing = await User.findOne({ where: { username } });
        if (existing) return res.status(409).json({ error: 'Username already taken' });
      }

      await user.update(updates);

      res.json({
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          full_name: user.full_name,
          role: user.role,
          phone: user.phone,
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async forgotPassword(req, res, next) {
    try {
      const { email } = req.body;

      const user = await User.findOne({ where: { email } });
      if (!user) {
        return res.json({ message: 'If the email exists, a verification code has been sent.' });
      }

      const code = Math.floor(100000 + Math.random() * 900000).toString();
      const resetExpires = new Date(Date.now() + 600000);

      await user.update({
        reset_password_token: code,
        reset_password_expires: resetExpires,
      });

      let transporter;
      try {
        const smtpPort = parseInt(process.env.SMTP_PORT || '587');
        transporter = require('nodemailer').createTransport({
          host: process.env.SMTP_HOST || 'smtp.ethereal.email',
          port: smtpPort,
          secure: smtpPort === 465,
          auth: {
            user: process.env.SMTP_USER || '',
            pass: process.env.SMTP_PASS || '',
          },
        });

        await transporter.sendMail({
          from: '"Tok Tokkie Shuttles" <noreply@shuttlesnamibia.com>',
          to: email,
          subject: 'Your Password Reset Code',
          html: `<p>Hi ${user.full_name},</p>
                 <p>You requested a password reset. Use the code below to reset your password:</p>
                 <p style="font-size:28px;font-weight:bold;letter-spacing:6px;text-align:center;padding:16px;background:#f3f4f6;border-radius:8px;">${code}</p>
                 <p>This code expires in 10 minutes.</p>
                 <p>If you didn't request this, please ignore this email.</p>
                 <p>— Tok Tokkie Shuttles Namibia</p>`,
        });
      } catch (emailErr) {
        console.warn('[Auth] Email not configured, reset code:', code);
      }

      res.json({ message: 'If the email exists, a verification code has been sent.' });
    } catch (error) {
      next(error);
    }
  }

  async verifyResetCode(req, res, next) {
    try {
      const { email, code } = req.body;

      const user = await User.findOne({ where: { email } });
      if (!user || !user.reset_password_token || !user.reset_password_expires) {
        return res.status(400).json({ error: 'Invalid or expired code' });
      }

      if (user.reset_password_token !== code) {
        return res.status(400).json({ error: 'Invalid verification code' });
      }

      if (new Date() > user.reset_password_expires) {
        return res.status(400).json({ error: 'Verification code has expired. Request a new one.' });
      }

      const tempToken = jwt.sign(
        { id: user.id, purpose: 'password_reset' },
        config.jwt.secret,
        { expiresIn: '15m' }
      );

      res.json({ token: tempToken });
    } catch (error) {
      next(error);
    }
  }

  async resetPassword(req, res, next) {
    try {
      const { token, password } = req.body;

      let decoded;
      try {
        decoded = jwt.verify(token, config.jwt.secret);
      } catch (err) {
        return res.status(400).json({ error: 'Invalid or expired reset token' });
      }

      if (decoded.purpose !== 'password_reset') {
        return res.status(400).json({ error: 'Invalid reset token' });
      }

      const user = await User.findByPk(decoded.id);
      if (!user) {
        return res.status(400).json({ error: 'User not found' });
      }

      if (password.length < 6) {
        return res.status(400).json({ error: 'Password must be at least 6 characters' });
      }

      const salt = await bcrypt.genSalt(10);
      const password_hash = await bcrypt.hash(password, salt);

      await user.update({
        password_hash,
        reset_password_token: null,
        reset_password_expires: null,
      });

      res.json({ message: 'Password reset successful. You can now sign in.' });
    } catch (error) {
      next(error);
    }
  }
}

module.exports = new AuthController();
