const { User } = require('../models/user');
const { MyGroup } = require('../models/mygroup');
const { Circuit } = require('../models/circuit');
const { SendNotif } = require('../models/sendnotif');
const { Province } = require('../models/province');
const tools = require('../tools/general');
const shared_consts = require('../tools/shared_nodejs');
const server_constants = require('../tools/server_constants');
const CronMod = require('../modules/CronMod');

class UserService {
  /**
   * Get user profile with friends
   */
  async getUserProfile(idapp, username, usernameOrig, perm) {
    try {
      const profile = await User.getUserProfileByUsername(
        idapp,
        username,
        usernameOrig,
        false,
        perm
      );

      const friends = await User.getFriendsByUsername(idapp, usernameOrig);

      // Get extra info if viewing own profile
      if (username === usernameOrig) {
        const userProfile = await User.getExtraInfoByUsername(idapp, profile.username);
        profile.profile = userProfile;
      }

      return {
        user: profile,
        friends
      };

    } catch (error) {
      console.error('Error in getUserProfile:', error.message);
      throw error;
    }
  }

  /**
   * Get user activities profile (public version)
   */
  async getUserActivitiesProfile(idapp, username, usernameOrig, perm, isAuthenticated) {
    try {
      const profile = await User.getUserProfileByUsername(
        idapp,
        username,
        usernameOrig,
        false,
        perm
      );

      const friends = await User.getFriendsByUsername(idapp, usernameOrig);

      let userProfile;
      if (isAuthenticated) {
        userProfile = await User.getExtraInfoByUsername(idapp, profile.username);
      } else {
        userProfile = await User.getProfilePerActivitiesByUsername(idapp, profile.username);
        // Hide sensitive data for non-authenticated users
        profile.aportador_solidario = '';
        profile.date_reg = '';
        profile.email = '';
      }

      profile.profile = userProfile;

      return {
        user: profile,
        friends
      };

    } catch (error) {
      console.error('Error in getUserActivitiesProfile:', error.message);
      throw error;
    }
  }

  /**
   * Update user balance and get notifications
   */
  async updateUserBalance(idapp, username, circuitId, groupname, lastdr = '') {
    try {
      const userProfile = await User.getExtraInfoByUsername(idapp, username);

      const arrRecNotif = await SendNotif.findAllNotifByUsernameIdAndIdApp(
        username,
        lastdr,
        idapp,
        shared_consts.LIMIT_NOTIF_FOR_USER,
        shared_consts.QualiNotifs.OTHERS
      );

      const arrRecNotifCoins = await SendNotif.findAllNotifByUsernameIdAndIdApp(
        username,
        lastdr,
        idapp,
        shared_consts.LIMIT_NOTIFCOINS_FOR_USER,
        shared_consts.QualiNotifs.CIRCUITS
      );

      return {
        userprofile: userProfile,
        arrrecnotif: arrRecNotif,
        arrrecnotifcoins: arrRecNotifCoins
      };

    } catch (error) {
      console.error('Error in updateUserBalance:', error.message);
      throw error;
    }
  }

  /**
   * Get user's friends list
   */
  async getUserFriends(idapp, username) {
    try {
      return await User.getFriendsByUsername(idapp, username);
    } catch (error) {
      console.error('Error in getUserFriends:', error.message);
      throw error;
    }
  }

  /**
   * Get user's groups
   */
  async getUserGroups(idapp, username, req) {
    try {
      return await MyGroup.getGroupsByUsername(idapp, username, req);
    } catch (error) {
      console.error('Error in getUserGroups:', error.message);
      throw error;
    }
  }

  /**
   * Get user's circuits
   */
  async getUserCircuits(idapp, username, user, nummovTodownload) {
    try {
      return await Circuit.getCircuitsByUsername(
        idapp,
        username,
        user,
        nummovTodownload
      );
    } catch (error) {
      console.error('Error in getUserCircuits:', error.message);
      throw error;
    }
  }

  /**
   * Execute friend command (add, remove, handshake, etc.)
   */
  async executeFriendCommand(req, idapp, usernameOrig, usernameDest, cmd, value) {
    try {
      // Normalize usernames
      const realUsernameOrig = await User.getRealUsernameByUsername(idapp, usernameOrig);
      const realUsernameDest = await User.getRealUsernameByUsername(idapp, usernameDest);

      return await User.setFriendsCmd(
        req,
        idapp,
        realUsernameOrig,
        realUsernameDest,
        cmd,
        value
      );

    } catch (error) {
      console.error('Error in executeFriendCommand:', error.message);
      throw error;
    }
  }

  /**
   * Execute group command
   */
  async executeGroupCommand(idapp, usernameOrig, groupnameDest, cmd, value, usernameLogged) {
    try {
      return await User.setGroupsCmd(
        idapp,
        usernameOrig,
        groupnameDest,
        cmd,
        value,
        usernameLogged
      );
    } catch (error) {
      console.error('Error in executeGroupCommand:', error.message);
      throw error;
    }
  }

  /**
   * Execute circuit command
   */
  async executeCircuitCommand(idapp, usernameOrig, circuitname, cmd, value, usernameLogged, extrarec) {
    try {
      const result = await User.setCircuitCmd(
        idapp,
        usernameOrig,
        circuitname,
        cmd,
        value,
        usernameLogged,
        extrarec
      );

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

      return result;

    } catch (error) {
      console.error('Error in executeCircuitCommand:', error.message);
      throw error;
    }
  }

  /**
   * Mark notification as read
   */
  async markNotificationAsRead(idapp, username, idnotif) {
    try {
      if (idnotif) {
        await SendNotif.setNotifAsRead(idapp, username, idnotif);
      }
    } catch (error) {
      console.error('Error in markNotificationAsRead:', error.message);
      // Non-critical error, don't throw
    }
  }

  /**
   * Check if username exists
   */
  async checkUsernameExists(idapp, username) {
    try {
      let user = await User.findByUsername(idapp, username, false, true);
      
      if (!user) {
        user = await User.findByUsernameTelegram(idapp, username, false, true);
      }

      return !!user;

    } catch (error) {
      console.error('Error in checkUsernameExists:', error.message);
      throw error;
    }
  }

  /**
   * Set user permissions
   */
  async setUserPermissions(userId, data) {
    try {
      await User.setPermissionsById(userId, data);
    } catch (error) {
      console.error('Error in setUserPermissions:', error.message);
      throw error;
    }
  }

  /**
   * Execute database operation (admin only)
   */
  async executeDbOperation(idapp, mydata, req, res) {
    try {
      const cronMod = new CronMod();
      return await cronMod.eseguiDbOp(idapp, mydata, req, res);
    } catch (error) {
      console.error('Error in executeDbOperation:', error.message);
      throw error;
    }
  }

  /**
   * Execute user-specific database operation
   */
  async executeUserDbOperation(idapp, mydata, username) {
    try {
      let result = await User.DbOp(idapp, mydata);

      // Handle specific operations
      await this._handleSpecificDbOperations(mydata);

      if (!result) {
        result = {};
      }

      return await User.updateMyData(result, idapp, username);

    } catch (error) {
      console.error('Error in executeUserDbOperation:', error.message);
      throw error;
    }
  }

  /**
   * Get map information (provinces with user counts)
   */
  async getMapInformation(idapp) {
    try {
      const query = [
        {
          $lookup: {
            from: 'users',
            localField: 'prov',
            foreignField: 'profile.resid_province',
            as: 'users'
          }
        },
        {
          $addFields: {
            userCount: { $size: '$users' }
          }
        },
        {
          $lookup: {
            from: 'provinces',
            localField: 'prov',
            foreignField: 'prov',
            as: 'provinceInfo'
          }
        },
        {
          $addFields: {
            provinceDescr: { $arrayElemAt: ['$provinceInfo.descr', 0] }
          }
        },
        {
          $project: {
            _id: 0,
            province: '$prov',
            descr: '$provinceDescr',
            userCount: 1,
            lat: 1,
            long: 1
          }
        }
      ];

      return await Province.aggregate(query);

    } catch (error) {
      console.error('Error in getMapInformation:', error.message);
      throw error;
    }
  }

  /**
   * Send command to user
   */
  async sendCommand(req, idapp, usernameOrig, usernameDest, cmd, value) {
    try {
      const realUsernameOrig = await User.getRealUsernameByUsername(idapp, usernameOrig);
      const realUsernameDest = await User.getRealUsernameByUsername(idapp, usernameDest);

      return await User.sendCmd(
        req,
        idapp,
        realUsernameOrig,
        realUsernameDest,
        cmd,
        value
      );

    } catch (error) {
      console.error('Error in sendCommand:', error.message);
      throw error;
    }
  }

  /**
   * Get user panel information (admin/manager view)
   */
  async getUserPanelInfo(idapp, username) {
    try {
      const user = await User.findOne(
        { idapp, username },
        {
          username: 1,
          name: 1,
          surname: 1,
          email: 1,
          verified_by_aportador: 1,
          aportador_solidario: 1,
          lasttimeonline: 1,
          deleted: 1,
          sospeso: 1,
          blocked: 1,
          reported: 1,
          username_who_report: 1,
          date_report: 1,
          profile: 1
        }
      ).lean();

      if (!user) {
        throw new Error('Utente non trovato');
      }

      return user;

    } catch (error) {
      console.error('Error in getUserPanelInfo:', error.message);
      throw error;
    }
  }

  /**
   * Handle specific database operations
   * @private
   */
  async _handleSpecificDbOperations(mydata) {
    const operations = {
      'saveStepTut': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.stepTutorial': mydata.value } }
      ),
      'noNameSurname': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.noNameSurname': mydata.value } }
      ),
      'telegram_verification_skipped': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.telegram_verification_skipped': mydata.value } }
      ),
      'noCircuit': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.noCircuit': mydata.value } }
      ),
      'noComune': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.noComune': mydata.value } }
      ),
      'noCircIta': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.noCircIta': mydata.value } }
      ),
      'insert_circuito_ita': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.insert_circuito_ita': mydata.value } }
      ),
      'noFoto': () => User.findOneAndUpdate(
        { _id: mydata._id },
        { $set: { 'profile.noFoto': mydata.value } }
      ),
      'pwdLikeAdmin': () => User.setPwdComeQuellaDellAdmin(mydata),
      'ripristinaPwdPrec': () => User.ripristinaPwdPrec(mydata)
    };

    const operation = operations[mydata.dbop];
    if (operation) {
      await operation();
    }
  }
}

module.exports = UserService;