const UserService = require('../services/UserService');
const AuthService = require('../services/AuthService');
const RegistrationService = require('../services/RegistrationService');
const { validateRegistration, validateLogin } = require('../validators/userValidators');
const telegrambot = require('../telegram/telegrambot');
const tools = require('../tools/general');
const server_constants = require('../tools/server_constants');
const shared_consts = require('../tools/shared_nodejs');

class UserController {
  constructor() {
    this.userService = new UserService();
    this.authService = new AuthService();
    this.registrationService = new RegistrationService();
  }

  /**
   * Register new user
   * POST /users
   */
  async register(req, res) {
    try {
      tools.mylog('POST /users - Registration');

      // Validate input
      const validationError = validateRegistration(req.body);
      if (validationError) {
        await tools.snooze(5000);
        return res.status(400).send({
          code: validationError.code,
          msg: validationError.message,
        });
      }

      const userData = this._extractUserData(req.body);
      userData.ipaddr = tools.getiPAddressUser(req);

      // Check security (IP bans, block words, etc.)
      const securityCheck = await this._performSecurityChecks(userData, req);
      if (securityCheck.blocked) {
        return res.status(securityCheck.status).send({
          code: securityCheck.code,
          msg: securityCheck.message,
        });
      }

      // Process registration
      const result = await this.registrationService.registerUser(userData, req);

      if (result.error) {
        return res.status(400).send({
          code: result.code,
          msg: result.message,
        });
      }

      // Send response with tokens
      res.header('x-auth', result.token).header('x-refrtok', result.refreshToken).header('x-browser-random', result.browser_random).send(result.user);
    } catch (error) {
      console.error('Error in registration:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * User login
   * POST /users/login
   */
  async login(req, res) {
    try {
      console.log('LOGIN');
      const { username, password, idapp, keyappid, br } = req.body;

      const browser_random = br;

      // Validate API key
      if (keyappid !== process.env.KEY_APP_ID) {
        return res.status(400).send({ code: server_constants.RIS_CODE_ERR });
      }

      // Validate input
      const validationError = validateLogin(req.body);
      if (validationError) {
        return res.status(400).send({
          code: validationError.code,
          msg: validationError.message,
        });
      }

      // Attempt login
      const result = await this.authService.authenticate(idapp, username, password, req, browser_random);

      console.log('attempt...', result);

      if (result.error) {
        return res.status(result.status).send({
          code: result.code,
          msg: result.message,
        });
      }

      // Send response with tokens
      res.header('x-auth', result.token).header('x-refrtok', result.refreshToken).header('x-browser-random', result.browser_random).send({
        usertosend: result.user,
        code: server_constants.RIS_CODE_OK,
        subsExistonDb: result.subsExistonDb,
      });
    } catch (error) {
      console.error('Error in login:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_LOGIN_ERR_GENERIC,
        msgerr: error.message,
      });
    }
  }

  /**
   * Get user profile
   * POST /users/profile
   */
  async getProfile(req, res) {
    try {
      const usernameOrig = req.user?.username || '';
      const perm = req.user?.perm || tools.Perm.PERM_NONE;
      const { username, idapp, idnotif } = req.body;

      // Mark notification as read if present
      if (idnotif) {
        await this.userService.markNotificationAsRead(idapp, usernameOrig, idnotif);
      }

      // Get user profile
      const profile = await this.userService.getUserProfile(idapp, username, usernameOrig, perm);

      res.send(profile);
    } catch (error) {
      console.error('Error in getProfile:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Update user saldo (balance)
   * POST /users/updatesaldo
   */
  async updateSaldo(req, res) {
    try {
      const username = req.user.username;
      const { idapp, circuitId, groupname, lastdr } = req.body;

      const result = await this.userService.updateUserBalance(idapp, username, circuitId, groupname, lastdr);

      res.send({ ris: result });
    } catch (error) {
      console.error('Error in updateSaldo:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Get user's friends
   * POST /users/friends
   */
  async getFriends(req, res) {
    try {
      const username = req.user.username;
      const { idapp } = req.body;

      const friends = await this.userService.getUserFriends(idapp, username);

      res.send(friends);
    } catch (error) {
      console.error('Error in getFriends:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Execute friend command (add, remove, etc.)
   * POST /users/friends/cmd
   */
  async executeFriendCommand(req, res) {
    try {
      const usernameLogged = req.user.username;
      const { idapp, usernameOrig, usernameDest, cmd, value } = req.body;

      // Security check
      if (!this._canExecuteFriendCommand(req.user, usernameOrig, usernameDest, cmd)) {
        return res.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED).send({
          code: server_constants.RIS_CODE_ERR_UNAUTHORIZED,
          msg: '',
        });
      }

      const result = await this.userService.executeFriendCommand(req, idapp, usernameOrig, usernameDest, cmd, value);

      res.send(result);
    } catch (error) {
      console.error('Error in executeFriendCommand:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Get user's groups
   * POST /users/groups
   */
  async getGroups(req, res) {
    try {
      const username = req.user.username;
      const { idapp } = req.body;

      const groups = await this.userService.getUserGroups(idapp, username, req);

      res.send(groups);
    } catch (error) {
      console.error('Error in getGroups:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Get user's circuits
   * POST /users/circuits
   */
  async getCircuits(req, res) {
    try {
      const username = req.user.username;
      const { idapp, nummovTodownload } = req.body;

      const circuits = await this.userService.getUserCircuits(idapp, username, req.user, nummovTodownload);

      res.send(circuits);
    } catch (error) {
      console.error('Error in getCircuits:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Refresh authentication token
   * POST /users/newtok
   */
  async refreshToken(req, res) {
    try {
      const { refreshToken, br } = req.body;

      const browser_random = br;

      if (!refreshToken) {
        return res.status(400).send({ error: 'Refresh token mancante' });
      }

      const result = await this.authService.refreshToken(refreshToken, req);

      if (result.error) {
        return res.status(result.status).send({ error: result.message });
      }

      res.status(200).send({
        token: result.token,
        refreshToken: result.refreshToken,
        browser_random,
      });
    } catch (error) {
      console.error('Error in refreshToken:', error.message);
      res.status(500).send({ error: 'Errore interno del server' });
    }
  }

  /**
   * Logout user (remove token)
   * DELETE /users/me/token
   */
  async logout(req, res) {
    try {
      await this.authService.removeToken(req.user, req.token);
      res.status(200).send();
    } catch (error) {
      console.error('Error in logout:', error.message);
      res.status(400).send();
    }
  }

  /**
   * Check if username exists
   * GET /users/:idapp/:username
   */
  async checkUsername(req, res) {
    try {
      const { username, idapp } = req.params;

      const exists = await this.userService.checkUsernameExists(idapp, username);

      if (!exists) {
        return res.status(404).send();
      }

      res.status(200).send();
    } catch (error) {
      console.error('Error in checkUsername:', error.message);
      res.status(400).send();
    }
  }

  /**
   * Update user permissions
   * POST /users/setperm
   */
  async setPermissions(req, res) {
    try {
      const { idapp, username, perm } = req.body;

      await this.userService.setUserPermissions(req.user._id, {
        idapp,
        username,
        perm,
      });

      res.status(200).send();
    } catch (error) {
      console.error('Error in setPermissions:', error.message);
      res.status(400).send();
    }
  }

  /**
   * Database operations (admin only)
   * POST /users/dbop
   */
  async executeDbOperation(req, res) {
    try {
      const { mydata, idapp } = req.body;

      // Check permissions
      if (!this._hasAdminPermissions(req.user)) {
        return res.status(404).send({
          code: server_constants.RIS_CODE_ERR_UNAUTHORIZED,
        });
      }

      const result = await this.userService.executeDbOperation(idapp, mydata, req, res);

      res.send({
        code: server_constants.RIS_CODE_OK,
        data: result,
      });
    } catch (error) {
      console.error('Error in executeDbOperation:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  /**
   * Get map information
   * POST /users/infomap
   */
  async getMapInfo(req, res) {
    try {
      const { idapp } = req.body;

      const mapData = await this.userService.getMapInformation(idapp);

      res.send({
        code: server_constants.RIS_CODE_OK,
        ris: mapData,
      });
    } catch (error) {
      console.error('Error in getMapInfo:', error.message);
      res.status(400).send({
        code: server_constants.RIS_CODE_ERR,
        msg: error.message,
      });
    }
  }

  // ===== PRIVATE HELPER METHODS =====

  _extractUserData(body) {
    const fields = [
      'email',
      'password',
      'username',
      'group',
      'name',
      'surname',
      'idapp',
      'keyappid',
      'lang',
      'profile',
      'aportador_solidario',
    ];

    const userData = {};
    fields.forEach((field) => {
      if (body[field] !== undefined) {
        userData[field] = body[field];
      }
    });

    // Normalize data
    if (userData.email) userData.email = userData.email.toLowerCase().trim();
    if (userData.username) userData.username = userData.username.trim();
    if (userData.name) userData.name = userData.name.trim();
    if (userData.surname) userData.surname = userData.surname.trim();

    return userData;
  }

  async _performSecurityChecks(userData, req) {
    const { User } = require('../models/user');

    // Check for blocked words
    if (tools.blockwords(userData.username) || tools.blockwords(userData.name) || tools.blockwords(userData.surname)) {
      await tools.snooze(5000);
      return {
        blocked: true,
        status: 404,
        code: server_constants.RIS_CODE_ERR,
      };
    }

    // Check IP ban
    const lastRecord = await User.getLastRec(userData.idapp);
    if (lastRecord && process.env.LOCALE !== '1') {
      if (lastRecord.ipaddr === userData.ipaddr && lastRecord.date_reg) {
        const isTooRecent = tools.isdiffSecDateLess(lastRecord.date_reg, 3);
        if (isTooRecent) {
          const msg = `${userData.ipaddr}: [${userData.username}] ${userData.name} ${userData.surname}`;
          tools.writeIPToBan(msg);
          await telegrambot.sendMsgTelegramToTheAdmin(userData.idapp, '‼️ BAN: ' + msg, true);
          await tools.snooze(5000);

          return {
            blocked: true,
            status: 400,
            code: server_constants.RIS_CODE_BANIP,
          };
        }
      }
    }

    return { blocked: false };
  }

  _canExecuteFriendCommand(user, usernameOrig, usernameDest, cmd) {
    const { User } = require('../models/user');

    if (User.isAdmin(user.perm) || User.isManager(user.perm)) {
      return true;
    }

    const allowedCommands = [shared_consts.FRIENDSCMD.SETFRIEND, shared_consts.FRIENDSCMD.SETHANDSHAKE];

    if (allowedCommands.includes(cmd)) {
      return usernameOrig === user.username || usernameDest === user.username;
    }

    return false;
  }

  _hasAdminPermissions(user) {
    const { User } = require('../models/user');
    return User.isCollaboratore(user.perm);
  }
}

module.exports = UserController;
