const path = require('path');
const { models } = require(path.resolve(process.env.ROOT_PATH, './db/db.js'));
const BaseController = require(path.resolve(process.env.ROOT_PATH, './controllers/BaseController.js'));
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const { format, differenceInHours, addHours } = require('date-fns');

class AuthController extends BaseController {
  constructor() {
    super();

    this.protected('me', 'refreshToken', 'login');
    this.admin('register');
  }

  /**
   * @swagger
   * /auth/login:
   *   post:
   *     summary: Authenticate a user
   *     tags: [Authentication]
   *     requestBody:
   *       required: true
   *       content:
   *         application/json:
   *           schema:
   *             type: object
   *             required:
   *               - username
   *               - password
   *             properties:
   *               username:
   *                 type: string
   *                 description: Username or email
   *               password:
   *                 type: string
   *                 format: password
   *     responses:
   *       200:
   *         description: Login successful
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 api_key:
   *                   type: string
   *                   description: JWT token for authentication
   *       401:
   *         description: Invalid credentials
   */
  async login(req, res) {
    const { username, password } = req.body;
    const user = await models.User.query().where('username', username).orWhere('email', username).first();
    if (!user) {
      return res.status(401).json({ error: 'Invalid username or password' });
    }
    const passwordMatch = await bcrypt.compare(password, user.password);
    if (!passwordMatch) {
      return res.status(401).json({ error: 'Invalid username or password' });
    }
    const token = jwt.sign({ id: user.id, username: user.username, is_admin: user.is_admin, email: user.email }, process.env.JWT_SECRET, { expiresIn: '6h' });
    res.json({ api_key: token });
  }

  /**
   * @swagger
   * /auth/me:
   *   get:
   *     summary: Get current user details
   *     tags: [Authentication]
   *     security:
   *       - bearerAuth: []
   *     responses:
   *       200:
   *         description: User details
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 id:
   *                   type: integer
   *                 username:
   *                   type: string
   *                 email:
   *                   type: string
   *                 is_admin:
   *                   type: boolean
   *                 is_active:
   *                   type: boolean
   *                 created:
   *                   type: string
   *                   format: date-time
   *                 modified:
   *                   type: string
   *                   format: date-time
   *                 token_expiration:
   *                   type: string
   *                   format: date-time
   *                 is_token_expired:
   *                   type: boolean
   *       401:
   *         description: Unauthorized
   */
  async me(req, res) {
    const user = await models.User.query().findById(req.user.id);
    
    const token = req.headers.authorization?.split(' ')[1];
    
    const decodedToken = token ? jwt.decode(token) : null;
    const tokenExpiration = decodedToken?.exp ? new Date(decodedToken.exp * 1000) : null;
    const isTokenExpired = tokenExpiration ? tokenExpiration < new Date() : true;
    
    res.json({
      id: user.id,
      username: user.username,
      email: user.email,
      is_admin: user.is_admin,
      is_active: user.is_active,
      created: user.created,
      modified: user.modified,
      token_expiration: tokenExpiration,
      is_token_expired: isTokenExpired
    });
  }

  /**
   * @swagger
   * /auth/refresh-token:
   *   post:
   *     summary: Refresh authentication token
   *     tags: [Authentication]
   *     security:
   *       - bearerAuth: []
   *     responses:
   *       200:
   *         description: Token refreshed successfully
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 api_key:
   *                   type: string
   *                   description: New JWT token
   *       401:
   *         description: Unauthorized
   */
  async refreshToken(req, res) {
    const user = await models.User.query().findById(req.user.id);
    const token = jwt.sign({ id: user.id, username: user.username, is_admin: user.is_admin, email: user.email }, process.env.JWT_SECRET, { expiresIn: '6h' });
    res.json({ api_key: token });
  }

  async registerView(req, res) {
    res.render('auth/register');
  }

  async register(req, res) {
    const { username, email, password } = req.body;
    const invite_token = crypto.randomBytes(32).toString('hex');

    const salt = await bcrypt.genSalt(10);
    const passwordEncrypted = await bcrypt.hash(password, salt);
    const user = await models.User.query().insert({
      username,
      password: passwordEncrypted,
      email,
      is_admin: 0,
      is_active: 0,
      created: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
      modified: format(new Date(), 'yyyy-MM-dd HH:mm:ss')
    });

    await models.Invite.query().insert({
      user_id: user.id,
      token: invite_token,
      expires_at: format(addHours(new Date(), 12), 'yyyy-MM-dd HH:mm:ss')
    });
    res.json({
      message: 'User registered successfully',
      invite_token
    });
  }

  async activateView(req, res) {
    res.render('auth/activate');
  }

  async activate(req, res) {
    const { invite_token } = req.body;
    const invite = await models.Invite.query().where('token', invite_token).first();
    if (!invite) {
      return res.status(400).json({ error: 'Invalid invite token' });
    }

    if (differenceInHours(new Date(), new Date(invite.expires_at)) > 0) {
      return res.status(400).json({ error: 'Invite token expired' });
    }

    const user = await models.User.query().where('id', invite.user_id).first();
    if (!user) {
      return res.status(400).json({ error: 'User not found' });
    }

    await models.User.query().update({
      is_active: 1,
      modified: format(new Date(), 'yyyy-MM-dd HH:mm:ss')
    }).where('id', user.id);

    res.json({
      message: 'User activated successfully',
      invite_token
    });
  }
}

module.exports = new AuthController();