import {
  BadRequestException,
  HttpStatus,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import mongoose, { Model } from 'mongoose';
import { LoginDto } from 'src/users/dto/login.dto';
import { CustomException } from 'src/exception/custom.exception';
import { Otp, OtpDocument } from 'src/schema/otp.schema';
import { User, UserDocument } from 'src/schema/user.schema';
import { ErrorMsg, SuccessMsg } from 'src/utils/response-message.helper';
import { VerifyOtpDto } from './dto/verify-otp.dto';
import { Request, Response } from 'express';
import {
  Map_User_Device,
  Map_User_Device_Document,
} from '../schema/map-user-device.schema';
import { JwtService } from '@nestjs/jwt';
import { SignUpDto } from './dto/signup.dto';
import * as bcrypt from 'bcrypt';
import { ResendOtpDto } from './dto/resend-otp.dto';
import { ChangePasswordDto } from './dto/change-password.dto';
import { CreateInquiryDto } from './dto/create-inquiry.dto';
import { Shipment, ShipmentDocument } from 'src/schema/shipment.schema';
import { Inquiry, InquiryDocument } from 'src/schema/inquiry.schema';
import {
  Map_Company_Employee,
  Map_Company_Employee_Document,
} from 'src/schema/map-company-employees.schema';
import { LogoutDto } from './dto/logout.dto';
import {
  Notification,
  NotificationDocument,
} from 'src/schema/notifications.schema';
import {
  Map_Shipment_Attachment,
  Map_Shipment_Attachment_Document,
} from 'src/schema/map-shipment-attachment';
import {
  ADMIN,
  COMPLETED,
  EN,
  USER,
  VERIFY_PHONE_NUMBER,
} from 'src/utils/constants';
import { SendMsg91OtpService } from 'src/utils/send-msg91-otp.helper';
import { Admin, AdminDocument } from 'src/schema/admin.schema';
import { modifyOtpNumber } from 'src/utils/modifyOtpNumber.helper';

@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User.name) private readonly userModel: Model<UserDocument>,
    @InjectModel(Otp.name) private readonly otpModel: Model<OtpDocument>,
    @InjectModel(Map_User_Device.name)
    private readonly mapUserDeviceModel: Model<Map_User_Device_Document>,
    private readonly jwtService: JwtService,
    @InjectModel(Shipment.name)
    private readonly shipmentModel: Model<ShipmentDocument>,
    @InjectModel(Inquiry.name)
    private readonly inquiryModel: Model<InquiryDocument>,
    @InjectModel(Map_Company_Employee.name)
    private readonly mapCompanyEmployeeModel: Model<Map_Company_Employee_Document>,
    @InjectModel(Notification.name)
    private readonly notificationModel: Model<NotificationDocument>,
    @InjectModel(Map_Shipment_Attachment.name)
    private readonly mapShipmentAttachmentModel: Model<Map_Shipment_Attachment_Document>,
    private readonly msg91OtpService: SendMsg91OtpService,
    @InjectModel(Admin.name) private readonly adminModel: Model<AdminDocument>,
  ) {}

  /***************************************SIGNUP*****************************************/
  async SignUp(lang: string, body: SignUpDto, res: Response) {
    try {
      if (body?.password !== body?.confirm_password) {
        throw new BadRequestException(
          ErrorMsg?.PASSWORD_DOES_NOT_MATCH[lang || EN],
        );
      }
      const findUser = await this.userModel.findOne({
        $or: [
          { username: body?.username },
          {
            phone_number: body?.phone_number,
            country_phone_code: body?.country_phone_code,
          },
        ],
        deleted_at: null,
      });
      if (findUser && findUser?.last_login !== null) {
        throw new BadRequestException(
          ErrorMsg?.USER_ALREADY_REGISTERED[lang || EN],
        );
      }
      const hashedPassword = await bcrypt.hash(body?.password, 10);
      let newUser;
      if (findUser) {
        await this.userModel.findByIdAndUpdate(findUser?._id, {
          first_name: body?.first_name,
          last_name: body?.last_name,
          username: body?.username,
          password: hashedPassword,
          country_phone_code: body?.country_phone_code,
        });
      } else {
        newUser = await new this.userModel({
          first_name: body?.first_name,
          last_name: body?.last_name,
          username: body?.username,
          password: hashedPassword,
          country_phone_code: body?.country_phone_code,
          phone_number: body?.phone_number,
        }).save();
      }
      const findDevice = await this.mapUserDeviceModel.findOne({
        device_id: body?.device_id,
        device_type: body?.device_type,
        user_id: new mongoose.Types.ObjectId(
          findUser ? findUser?._id : newUser?._id,
        ),
      });
      if (!findDevice) {
        await new this.mapUserDeviceModel({
          device_id: body?.device_id,
          device_token: body?.device_token,
          device_type: body?.device_type,
          user_id: new mongoose.Types.ObjectId(
            findUser ? findUser?._id : newUser?._id,
          ),
        }).save();
      } else {
        await this.mapUserDeviceModel.findByIdAndUpdate(findDevice?._id, {
          device_token: body?.device_token,
        });
      }
      const newOtp = Math.floor(100000 + Math.random() * 900000);
      await this.msg91OtpService.sendMsg91Otp(
        body?.country_phone_code,
        modifyOtpNumber(body?.phone_number),
        newOtp,
      );
      await new this.otpModel({
        phone_number: body?.phone_number,
        country_phone_code: body?.country_phone_code,
        action: VERIFY_PHONE_NUMBER,
        otp: newOtp,
        expired_at: new Date(new Date().getTime() + 2 * 60 * 1000),
      }).save();
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.OTP_SENT[lang || EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /***************************************RESEND OTP*************************************/
  async resendOtp(lang: string, body: ResendOtpDto, res: Response) {
    try {
      const user = await this.userModel.findOne({
        phone_number: body?.phone_number,
        country_phone_code: body?.country_phone_code,
        deleted_at: null,
      });
      if (!user) {
        throw new BadRequestException(ErrorMsg.USER_NOT_REGISTERED[lang || EN]);
      }
      if (user?.is_blocked) {
        throw new BadRequestException(
          ErrorMsg.YOUR_ACCOUNT_BLOCKED[lang || EN],
        );
      }
      const findOtp = await this.otpModel.findOne({
        phone_number: body?.phone_number,
        country_phone_code: body?.country_phone_code,
        action: body?.action,
        expired_at: { $gte: new Date() },
      });
      if (findOtp) {
        throw new BadRequestException(ErrorMsg.OTP_RETRY[lang || EN]);
      }
      const newOtp = Math.floor(100000 + Math.random() * 900000);
      await this.msg91OtpService.sendMsg91Otp(
        body?.country_phone_code,
        modifyOtpNumber(body?.phone_number),
        newOtp,
      );
      await new this.otpModel({
        phone_number: body?.phone_number,
        country_phone_code: body?.country_phone_code,
        action: body?.action,
        otp: newOtp,
        expired_at: new Date(new Date().getTime() + 2 * 60 * 1000),
      }).save();
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.OTP_SENT[lang || EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /***************************************VERIFY OTP*************************************/
  async verifyOtp(lang: string, body: VerifyOtpDto, res: Response) {
    try {
      const user = await this.userModel.findOne({
        phone_number: body?.phone_number,
        country_phone_code: body?.country_phone_code,
        deleted_at: null,
      });
      if (!user) {
        throw new BadRequestException(
          ErrorMsg?.USER_NOT_REGISTERED[lang || EN],
        );
      }
      const findOtp = await this.otpModel.findOne({
        phone_number: body?.phone_number,
        country_phone_code: body?.country_phone_code,
        action: body?.action,
        expired_at: { $gte: new Date() },
      });
      if (!findOtp) {
        throw new BadRequestException(ErrorMsg?.OTP_NOT_FOUND[lang || EN]);
      }
      if (findOtp?.otp !== body.otp) {
        throw new BadRequestException(ErrorMsg?.WRONG_OTP_ENTERED[lang || EN]);
      }
      const token = await this.jwtService.sign(
        { id: user?._id, role: USER },
        { secret: process.env.JWT_SECRET_KEY },
      );
      if (body?.action === VERIFY_PHONE_NUMBER) {
        const updateObj = { last_login: new Date() };
        if (!user?.phone_number_verified_at) {
          updateObj['phone_number_verified_at'] = new Date();
        }
        await this.userModel.findByIdAndUpdate(user?._id, updateObj);
      }
      await this.otpModel.findByIdAndDelete(findOtp?._id);
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.OTP_VERIFIED[lang || EN],
        role: user?.is_admin ? ADMIN : USER,
        token,
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /***************************************LOGIN SEND OTP*********************************/
  async login(lang: string, body: LoginDto, res: Response) {
    try {
      const user = await this.userModel.findOne({
        $or: [
          { username: body?.username },
          {
            phone_number: body?.phone_number,
            country_phone_code: body?.country_phone_code,
          },
        ],
        deleted_at: null,
      });
      if (!user) {
        const admin = await this.adminModel.findOne({ email: body?.username });
        if (!admin) {
          throw new BadRequestException(
            ErrorMsg?.USER_NOT_REGISTERED[lang || EN],
          );
        }
        const isMatch = await bcrypt.compare(body?.password, admin?.password);
        if (!isMatch) {
          throw new BadRequestException(ErrorMsg.WRONG_CREDENTIALS[lang || EN]);
        }
        const token = await this.jwtService.sign(
          { id: admin?._id, role: ADMIN },
          { secret: process.env.JWT_SECRET_KEY },
        );
        return res.status(HttpStatus.CREATED).json({
          success: true,
          statusCode: 201,
          message: body?.phone_number
            ? SuccessMsg.OTP_SENT[lang || EN]
            : SuccessMsg.LOGIN_SUCCESSFULL[lang || EN],
          token,
          role: ADMIN,
        });
      } else if (user?.last_login === null) {
        throw new BadRequestException(
          ErrorMsg?.USER_NOT_REGISTERED[lang || EN],
        );
      } else if (user?.is_blocked) {
        throw new BadRequestException(
          ErrorMsg.YOUR_ACCOUNT_BLOCKED[lang || EN],
        );
      }
      let token;
      if (body?.phone_number) {
        const findOtp = await this.otpModel.findOne({
          phone_number: body?.phone_number,
          country_phone_code: body?.country_phone_code,
          action: VERIFY_PHONE_NUMBER,
          expired_at: { $gte: new Date() },
        });
        if (findOtp) {
          throw new BadRequestException(ErrorMsg.OTP_RETRY[lang || EN]);
        }
        const newOtp = Math.floor(100000 + Math.random() * 900000);
        await this.msg91OtpService.sendMsg91Otp(
          body?.country_phone_code,
          modifyOtpNumber(body?.phone_number),
          newOtp,
        );
        await new this.otpModel({
          phone_number: body?.phone_number,
          country_phone_code: body?.country_phone_code,
          action: VERIFY_PHONE_NUMBER,
          otp: newOtp,
          expired_at: new Date(new Date().getTime() + 2 * 60 * 1000),
        }).save();
      } else {
        if (!user?.phone_number_verified_at) {
          throw new BadRequestException(
            ErrorMsg?.VERIFY_BEFORE_LOGIN[lang || EN],
          );
        }
        const isMatch = await bcrypt.compare(body?.password, user?.password);
        console.log("_isMatch",user?.password,isMatch)
        if (!isMatch) {
          throw new BadRequestException(ErrorMsg.WRONG_CREDENTIALS[lang || EN]);
        }
        token = await this.jwtService.sign(
          { id: user?._id, role: USER },
          { secret: process.env.JWT_SECRET_KEY },
        );
        await this.userModel.findByIdAndUpdate(user?._id, {
          last_login: new Date(),
        });
      }
      const findDevice = await this.mapUserDeviceModel.findOne({
        device_id: body?.device_id,
        device_type: body?.device_type,
        user_id: new mongoose.Types.ObjectId(user?._id),
      });
      if (!findDevice) {
        await new this.mapUserDeviceModel({
          device_id: body?.device_id,
          device_token: body?.device_token,
          device_type: body?.device_type,
          user_id: new mongoose.Types.ObjectId(user?._id),
        }).save();
      } else {
        await this.mapUserDeviceModel.findByIdAndUpdate(findDevice?._id, {
          device_token: body?.device_token,
        });
      }
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: body?.phone_number
          ? SuccessMsg.OTP_SENT[lang || EN]
          : SuccessMsg.LOGIN_SUCCESSFULL[lang || EN],
        token,
        role: user?.is_admin ? ADMIN : USER,
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /***************************************CHANGE PASSWORD********************************/
  async changePassword(
    req: Request,
    lang: string,
    body: ChangePasswordDto,
    res: Response,
  ) {
    try {
      if (body?.new_password !== body?.confirm_password) {
        throw new BadRequestException(
          ErrorMsg?.BOTH_PASSWORD_MATCH[lang || EN],
        );
      }
      if (body?.type === 'change_password') {
        const matchPassword = await bcrypt.compare(
          body?.old_password,
          req['user']['password'],
        );
        if (!matchPassword) {
          throw new BadRequestException(
            ErrorMsg?.OLD_PASSWORD_INCORRECT[lang ?? EN],
          );
        }
      }
      const hashedPassword = await bcrypt.hash(body?.new_password, 10);
      await this.userModel.findByIdAndUpdate(req['user']['_id'], {
        password: hashedPassword,
      });
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.PASSWORD_CHANGED[lang || EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /************************************GET MY SHIPMENTS**********************************/
  async getMyShipments(
    req: Request,
    lang: string,
    type: string,
    page = 1,
    limit = 10,
    job_id: string,
    res: Response,
  ) {
    try {
      const findCompany = await this.mapCompanyEmployeeModel.findOne({
        employee_id: new mongoose.Types.ObjectId(req['user']['_id']),
      });
      const query = {};
      if (job_id) {
        query['job_id'] = { $regex: `.*${job_id}.*`, $options: 'i' };
      }
      if (type) {
        query['current_state'] = type;
      }
      const data = await this.shipmentModel.aggregate([
        {
          $match: {
            company_id: {
              $in: [
                new mongoose.Types.ObjectId(findCompany?.company_id),
                new mongoose.Types.ObjectId(req['user']['_id']),
              ],
            },
            ...query,
          },
        },
        {
          $lookup: {
            from: 'map_shipment_commodities',
            as: 'commodity',
            localField: '_id',
            foreignField: 'shipment_id',
          },
        },
        {
          $lookup: {
            from: 'users',
            as: 'company',
            let: { company_id: '$company_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$company_id'] } } },
              {
                $project: {
                  first_name: 1,
                  last_name: 1,
                  company_name: 1,
                  username: 1,
                  email: 1,
                  phone_number: 1,
                },
              },
            ],
          },
        },
        { $unwind: { path: '$company' } },
        {
          $lookup: {
            from: 'shipment_modes',
            as: 'shipment_mode',
            let: { shipment_mode: '$shipment_mode' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$shipment_mode'] } } },
            ],
          },
        },
        {
          $unwind: { path: '$shipment_mode', preserveNullAndEmptyArrays: true },
        },
        {
          $lookup: {
            from: 'map_shipment_attachments',
            as: 'attachments',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                  name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_shipment_statuses',
            as: 'shipment_status',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  _id: 1,
                  shipment_id: 1,
                  status_en:
                    lang === 'AR'
                      ? {
                          $cond: {
                            if: { $ifNull: ['$status_ar', 0] },
                            then: '$status_ar',
                            else: '$status_en',
                          },
                        }
                      : '$status_en',
                  createdAt: 1,
                  updatedAt: 1,
                  __v: 1,
                },
              },
            ],
          },
        },
        {
          $addFields: {
            attachments: {
              $filter: {
                input: '$attachments',
                as: 'attachments',
                cond: { $eq: ['$$attachments.name', 'shipment_document'] },
              },
            },
            invoices: {
              $filter: {
                input: '$attachments',
                as: 'invoices',
                cond: {
                  $and: [
                    { $eq: ['$$invoices.name', 'shipment_invoice'] },
                    {
                      $or: [
                        { $eq: [req['user']['_id'], '$company._id'] },
                        { $eq: [findCompany?.show_invoice, true] },
                      ],
                    },
                  ],
                },
              },
            },
          },
        },
        {
          $addFields: {
            container_number: {
              $trim: {
                input: {
                  $reduce: {
                    input: '$container_number',
                    initialValue: '',
                    in: { $concat: ['$$value', '$$this', ', '] },
                  },
                },
                chars: ', ',
              },
            },
          },
        },
        { $project: { current_status_id: 0, company_id: 0 } },
        {
          $facet: {
            paginatedResults: [
              { $sort: { createdAt: -1 } },
              { $skip: (Number(page) - 1) * Number(limit) },
              { $limit: Number(limit) },
            ],
            totalCount: [{ $count: 'count' }],
          },
        },
        {
          $addFields: {
            total: { $ifNull: [{ $arrayElemAt: ['$totalCount.count', 0] }, 0] },
          },
        },
        { $project: { paginatedResults: 1, total: 1 } },
      ]);
      return res.status(HttpStatus.OK).json({
        success: true,
        statusCode: 200,
        current_page: Number(page),
        total_pages: Math.ceil(Number(data[0].total) / Number(limit)) || 0,
        limit: Number(limit),
        total: Number(data[0].total),
        data: data[0].paginatedResults,
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /********************************GET MY SHIPMENTS NEW**********************************/
  async getMyShipmentsNew(
    req: Request,
    lang: string,
    type: string,
    page = 1,
    limit = 10,
    job_id: string,
    res: Response,
    search: string,
  ) {
    try {
      const findCompanies = await this.mapCompanyEmployeeModel.find({
        employee_id: new mongoose.Types.ObjectId(req['user']['_id']),
      });
      const query = {};
      if (job_id) {
        query['job_id'] = { $regex: `.*${job_id}.*`, $options: 'i' };
      }
      if (type) {
        query['current_state'] = type;
      }
      const companies = findCompanies.map((item) => item?.company_id);
      companies.push(new mongoose.Types.ObjectId(req['user']['_id']));
      const data = await this.shipmentModel.aggregate([
        { $match: { company_id: { $in: companies }, ...query } },
        {
          $lookup: {
            from: 'map_shipment_commodities',
            as: 'commodity',
            localField: '_id',
            foreignField: 'shipment_id',
          },
        },
        {
          $lookup: {
            from: 'users',
            as: 'company',
            let: { company_id: '$company_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$company_id'] } } },
              {
                $project: {
                  first_name: 1,
                  last_name: 1,
                  company_name: 1,
                  username: 1,
                  email: 1,
                  phone_number: 1,
                },
              },
            ],
          },
        },
        { $unwind: { path: '$company' } },
        {
          $lookup: {
            from: 'shipment_modes',
            as: 'shipment_mode',
            let: { shipment_mode: '$shipment_mode' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$shipment_mode'] } } },
            ],
          },
        },
        {
          $unwind: { path: '$shipment_mode', preserveNullAndEmptyArrays: true },
        },
        {
          $lookup: {
            from: 'map_shipment_attachments',
            as: 'attachments',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                  name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_shipment_attachments',
            as: 'barcode',
            let: { shipment_id: '$_id' },
            pipeline: [
              {
                $match: {
                  $expr: { $eq: ['$shipment_id', '$$shipment_id'] },
                  name: 'barcode',
                },
              },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_company_employees',
            as: 'employee',
            let: { company_id: '$company_id' },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $and: [
                      { $eq: ['$company_id', '$$company_id'] },
                      { $eq: ['$employee_id', req['user']['_id']] },
                    ],
                  },
                },
              },
            ],
          },
        },
        { $unwind: { path: '$employee', preserveNullAndEmptyArrays: true } },
        {
          $lookup: {
            from: 'map_shipment_statuses',
            as: 'shipment_status',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  _id: 1,
                  shipment_id: 1,
                  status_en: 1,
                  status_ar: 1,
                  createdAt: 1,
                  updatedAt: 1,
                  __v: 1,
                },
              },
            ],
          },
        },

        {
          $addFields: {
            attachments: {
              $filter: {
                input: '$attachments',
                as: 'attachments',
                cond: { $eq: ['$$attachments.name', 'shipment_document'] },
              },
            },
            invoices: {
              $filter: {
                input: '$attachments',
                as: 'invoices',
                cond: {
                  $and: [
                    { $eq: ['$$invoices.name', 'shipment_invoice'] },
                    {
                      $or: [
                        { $eq: [req['user']['_id'], '$company._id'] },
                        { $eq: ['$employee.show_invoice', true] },
                      ],
                    },
                  ],
                },
              },
            },
          },
        },
        {
          $addFields: {
            container_number: {
              $trim: {
                input: {
                  $reduce: {
                    input: '$container_number',
                    initialValue: '',
                    in: { $concat: ['$$value', '$$this', ', '] },
                  },
                },
                chars: ', ',
              },
            },
          },
        },
        { $project: { current_status_id: 0, company_id: 0, employee: 0 } },

        ...(search
          ? (() => {
              const regex = new RegExp(search, 'i');
              return [
                {
                  $match: {
                    $or: [
                      { pickup_from_en: regex },
                      { pickup_from_ar: regex },
                      { delivered_to_en: regex },
                      { delivered_to_ar: regex },
                      { customer_name: regex },
                      { job_id: regex },
                      { serial_number: regex },
                      { agent : regex},
                      {person_name_agent:regex}, 
                      { 'commodity.name_en': regex },
                      { 'commodity.name_ar': regex },
                      { 'company.phone_number': regex },
                      { 'company.company_name': regex },
                      { 'company.first_name': regex },
                      { 'company.last_name': regex },
                      {'shipment_status.status_en' : regex},
                      {'shipment_status.status_ar' : regex},
                      {'shipment_mode.name_en' :regex},
                      {'shipment_mode.name_ar' :regex},
                      {container_number : regex}
                    ],
                  },
                },
              ];
            })()
          : []),
        {
          $facet: {
            paginatedResults: [
              { $sort: { createdAt: -1 } },
              { $skip: (Number(page) - 1) * Number(limit) },
              { $limit: Number(limit) },
            ],
            totalCount: [{ $count: 'count' }],
          },
        },
        {
          $addFields: {
            total: { $ifNull: [{ $arrayElemAt: ['$totalCount.count', 0] }, 0] },
          },
        },
        { $project: { paginatedResults: 1, total: 1 } },
      ]);
      return res.status(HttpStatus.OK).json({
        success: true,
        statusCode: 200,
        current_page: Number(page),
        total_pages: Math.ceil(Number(data[0].total) / Number(limit)) || 0,
        limit: Number(limit),
        total: Number(data[0].total),
        data: data[0].paginatedResults,
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /************************************CREATE INQUIRY************************************/
  async createInquiry(
    req: Request,
    lang: string,
    body: CreateInquiryDto,
    res: Response,
  ) {
    try {
      await new this.inquiryModel({
        user_id: new mongoose.Types.ObjectId(req['user']['_id']),
        ...body,
      }).save();
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.INQUIRY_POSTED[lang || EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /************************************GET MY INQUIRIES**********************************/
  async getMyInquiries(req: Request, page = 1, limit = 10, res: Response) {
    try {
      // const data = await this.inquiryModel
      //   .find({ user_id: new mongoose.Types.ObjectId(req['user']['_id']) })
      //   .sort({ createdAt: -1 })
      //   .skip((Number(page) - 1) * Number(limit))
      //   .limit(Number(limit));
      // const total = await this.inquiryModel.countDocuments({
      //   user_id: new mongoose.Types.ObjectId(req['user']['_id']),
      // });
      // return res.status(HttpStatus.OK).json({
      //   success: true,
      //   statusCode: 200,
      //   current_page: Number(page),
      //   total_pages: Math.ceil(total / Number(limit)) || 0,
      //   limit: Number(limit),
      //   total,
      //   data,
      // });
      const data = await this.inquiryModel.aggregate([
        {
          $match: { user_id: new mongoose.Types.ObjectId(req['user']['_id']) },
        },
        {
          $lookup: {
            from: 'map_inquiry_attachments',
            as: 'attachments',
            let: { inquiry_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$inquiry_id', '$$inquiry_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup:{
            from:"quotations",
            localField:"_id",
            foreignField:"inquiry_id",
            as:"quotation"
          }
        },
        {
          $unwind: { path: '$quotation', preserveNullAndEmptyArrays: true },
        },
        {
          $lookup: {
            from: 'map_inquiry_replies',
            as: 'replies',
            let: { inquiry_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$inquiry_id', '$$inquiry_id'] } } },
            ],
          },
        },
        {
          $facet: {
            paginatedResults: [
              { $sort: { createdAt: -1 } },
              { $skip: (Number(page) - 1) * Number(limit) },
              { $limit: Number(limit) },
            ],
            totalCount: [{ $count: 'count' }],
          },
        },
        {
          $addFields: {
            total: { $ifNull: [{ $arrayElemAt: ['$totalCount.count', 0] }, 0] },
          },
        },
        { $project: { paginatedResults: 1, total: 1 } },
      ]);
      return res.status(HttpStatus.OK).json({
        success: true,
        statusCode: 200,
        current_page: Number(page),
        total_pages: Math.ceil(Number(data[0].total) / Number(limit)) || 0,
        limit: Number(limit),
        total: Number(data[0].total),
        data: data[0].paginatedResults,
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /************************************LOG OUT*******************************************/
  async logout(req: Request, lang: string, body: LogoutDto, res: Response) {
    try {
      await this.mapUserDeviceModel.deleteMany({
        user_id: req['user']['_id'],
        device_id: body?.device_id,
      });
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.LOGOUT_SUCCESS[lang || EN],
      });
    } catch (error) {
      throw new BadRequestException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /************************************GET PROFILE***************************************/
  async getProfile(req: Request, res: Response) {
    try {
      const user = req['user'];
      delete user._doc.password;
      return res
        .status(HttpStatus.OK)
        .json({ success: true, statusCode: 200, user });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /***********************************GET MY NOTIFICATIONS*******************************/
  async getMyNotifications(
    req: Request,
    lang: string,
    page = 1,
    limit = 10,
    res: Response,
  ) {
    try {
      const data = await this.notificationModel.aggregate([
        {
          $match: { user_id: new mongoose.Types.ObjectId(req['user']['_id']) },
        },
        {
          $lookup: {
            from: 'shipments',
            as: 'shipment',
            let: { shipment_id: '$shipment_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$shipment_id'] } } },
            ],
          },
        },
        { $unwind: { path: '$shipment', preserveNullAndEmptyArrays: true } },
        {
          $project: {
            user_id: 1,
            shipment_id: 1,
            inquiry_id: 1,
            title_en: 1,
            title_ar: 1,
            body_en: 1,
            body_ar: 1,
            createdAt: 1,
            job_id: '$shipment.job_id',
            serial_number: '$shipment.serial_number',
          },
        },
        {
          $facet: {
            paginatedResults: [
              { $sort: { createdAt: -1 } },
              { $skip: (Number(page) - 1) * Number(limit) },
              { $limit: Number(limit) },
            ],
            totalCount: [{ $count: 'count' }],
          },
        },
        {
          $addFields: {
            total: { $ifNull: [{ $arrayElemAt: ['$totalCount.count', 0] }, 0] },
          },
        },
        { $project: { paginatedResults: 1, total: 1 } },
      ]);
      return res.status(HttpStatus.OK).json({
        success: true,
        statusCode: 200,
        current_page: Number(page),
        total_pages: Math.ceil(Number(data[0].total) / Number(limit)) || 0,
        limit: Number(limit),
        total: Number(data[0].total),
        data: data[0].paginatedResults,
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************UPLOAD SHIPMENT DOCUMENT*****************************/
  async uploadShipmentDocument(
    req: Request,
    lang: string,
    shipment_id: string,
    file: Express.Multer.File,
    res: Response,
  ) {
    try {
      if (req['fileValidationError']) {
        throw new BadRequestException(req['fileValidationError']);
      }
      if (!file) {
        throw new BadRequestException(
          ErrorMsg?.PROVIDE_VALID_DOCUMENT[lang || EN],
        );
      }
      const shipment = await this.shipmentModel.findById(shipment_id);
      if (!shipment) {
        throw new BadRequestException(ErrorMsg?.SHIPMENT_NOT_FOUND[lang || EN]);
      }
      if (shipment?.current_state === COMPLETED) {
        throw new BadRequestException(ErrorMsg?.CANT_ADD_DOCUMENT[lang || EN]);
      }
      await new this.mapShipmentAttachmentModel({
        name: file?.fieldname,
        file_name: file?.filename,
        original_name: file?.originalname,
        mime_type: file?.mimetype,
        type: 'document',
        shipment_id: new mongoose.Types.ObjectId(shipment_id),
        path: file?.path,
        base_url: `${req.protocol}://${req.headers.host}/`,
        uploaded_by: USER,
      }).save();
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.DOCUMENT_UPLOADED[lang ?? EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************DELETE MY INQUIRY************************************/
  async deleteMyInquiry(
    req: Request,
    lang: string,
    inquiry_id: string,
    res: Response,
  ) {
    try {
      await this.inquiryModel.findOneAndDelete({
        _id: new mongoose.Types.ObjectId(inquiry_id),
        user_id: new mongoose.Types.ObjectId(req['user']['_id']),
      });
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.INQUIRY_DELETED[lang ?? EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************CHANGE MY LANGUAGE***********************************/
  async changeMyLanguage(req: Request, lang: string, res: Response) {
    try {
      await this.userModel.findByIdAndUpdate(req['user']['_id'], { lang });
      return res.status(HttpStatus.CREATED).json({
        success: true,
        statusCode: 201,
        message: SuccessMsg?.LANGUAGE_CHANGED[lang ?? EN],
      });
    } catch (error) {
      throw new BadRequestException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************GET SINGLE SHIPMENT**********************************/
  async getSingleShipment(
    req: Request,
    shipment_id: string,
    lang: string,
    res: Response,
  ) {
    try {
      const findCompany = await this.mapCompanyEmployeeModel.findOne({
        employee_id: new mongoose.Types.ObjectId(req['user']['_id']),
      });
      const data = await this.shipmentModel.aggregate([
        {
          $match: {
            _id: new mongoose.Types.ObjectId(shipment_id),
            company_id: {
              $in: [
                new mongoose.Types.ObjectId(findCompany?.company_id),
                new mongoose.Types.ObjectId(req['user']['_id']),
              ],
            },
          },
        },
        {
          $lookup: {
            from: 'users',
            as: 'company',
            let: { company_id: '$company_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$company_id'] } } },
              {
                $project: {
                  first_name: 1,
                  last_name: 1,
                  company_name: 1,
                  username: 1,
                  email: 1,
                  phone_number: 1,
                },
              },
            ],
          },
        },
        { $unwind: { path: '$company' } },
        {
          $lookup: {
            from: 'map_shipment_commodities',
            as: 'commodity',
            localField: '_id',
            foreignField: 'shipment_id',
          },
        },
        {
          $lookup: {
            from: 'shipment_modes',
            as: 'shipment_mode',
            let: { shipment_mode: '$shipment_mode' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$shipment_mode'] } } },
            ],
          },
        },
        {
          $unwind: { path: '$shipment_mode', preserveNullAndEmptyArrays: true },
        },
        {
          $lookup: {
            from: 'map_shipment_attachments',
            as: 'attachments',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                  name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_shipment_statuses',
            as: 'shipment_status',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  _id: 1,
                  shipment_id: 1,
                  status_en: 1,
                  status_ar: 1,
                  createdAt: 1,
                  updatedAt: 1,
                  __v: 1,
                },
              },
            ],
          },
        },
        {
          $addFields: {
            attachments: {
              $filter: {
                input: '$attachments',
                as: 'attachments',
                cond: { $eq: ['$$attachments.name', 'shipment_document'] },
              },
            },
            invoices: {
              $filter: {
                input: '$attachments',
                as: 'invoices',
                cond: {
                  $and: [
                    { $eq: ['$$invoices.name', 'shipment_invoice'] },
                    {
                      $or: [
                        { $eq: [req['user']['_id'], '$company._id'] },
                        { $eq: [findCompany?.show_invoice, true] },
                      ],
                    },
                  ],
                },
              },
            },
          },
        },
        {
          $addFields: {
            container_number: {
              $trim: {
                input: {
                  $reduce: {
                    input: '$container_number',
                    initialValue: '',
                    in: { $concat: ['$$value', '$$this', ', '] },
                  },
                },
                chars: ', ',
              },
            },
          },
        },
        { $project: { current_status_id: 0, company_id: 0, is_verified: 1 } },
      ]);
      if (data.length === 0) {
        throw new BadRequestException(
          ErrorMsg?.SHIPMENT_NOT_FOUND[lang || 'EN'],
        );
      }
      return res
        .status(HttpStatus.OK)
        .json({ success: true, statusCode: 200, data: data[0] });
    } catch (error) {
      throw new BadRequestException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************GET SINGLE SHIPMENT**********************************/
  async getSingleShipmentNew(
    req: Request,
    shipment_id: string,
    lang: string,
    res: Response,
  ) {
    try {
      const findCompanies = await this.mapCompanyEmployeeModel.find({
        employee_id: new mongoose.Types.ObjectId(req['user']['_id']),
      });
      const companies = findCompanies.map((item) => item?.company_id);
      companies.push(new mongoose.Types.ObjectId(req['user']['_id']));
      const data = await this.shipmentModel.aggregate([
        {
          $match: {
            _id: new mongoose.Types.ObjectId(shipment_id),
            company_id: { $in: companies },
          },
        },
        {
          $lookup: {
            from: 'users',
            as: 'company',
            let: { company_id: '$company_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$company_id'] } } },
              {
                $project: {
                  first_name: 1,
                  last_name: 1,
                  company_name: 1,
                  username: 1,
                  email: 1,
                  phone_number: 1,
                },
              },
            ],
          },
        },
        { $unwind: { path: '$company' } },
        {
          $lookup: {
            from: 'map_shipment_commodities',
            as: 'commodity',
            localField: '_id',
            foreignField: 'shipment_id',
          },
        },
        {
          $lookup: {
            from: 'shipment_modes',
            as: 'shipment_mode',
            let: { shipment_mode: '$shipment_mode' },
            pipeline: [
              { $match: { $expr: { $eq: ['$_id', '$$shipment_mode'] } } },
            ],
          },
        },
        {
          $unwind: { path: '$shipment_mode', preserveNullAndEmptyArrays: true },
        },
        {
          $lookup: {
            from: 'map_shipment_attachments',
            as: 'attachments',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                  name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_shipment_attachments',
            as: 'barcode',
            let: { shipment_id: '$_id' },
            pipeline: [
              {
                $match: {
                  $expr: { $eq: ['$shipment_id', '$$shipment_id'] },
                  name: 'barcode',
                },
              },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_shipment_statuses',
            as: 'shipment_status',
            let: { shipment_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$shipment_id', '$$shipment_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  _id: 1,
                  shipment_id: 1,
                  status_en: 1,
                  status_ar: 1,
                  createdAt: 1,
                  updatedAt: 1,
                  __v: 1,
                },
              },
            ],
          },
        },
        {
          $lookup: {
            from: 'map_company_employees',
            as: 'employee',
            let: { company_id: '$company_id' },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $and: [
                      { $eq: ['$company_id', '$$company_id'] },
                      { $eq: ['$employee_id', req['user']['_id']] },
                    ],
                  },
                },
              },
            ],
          },
        },
        { $unwind: { path: '$employee', preserveNullAndEmptyArrays: true } },
        {
          $addFields: {
            attachments: {
              $filter: {
                input: '$attachments',
                as: 'attachments',
                cond: { $eq: ['$$attachments.name', 'shipment_document'] },
              },
            },
            invoices: {
              $filter: {
                input: '$attachments',
                as: 'invoices',
                cond: {
                  $and: [
                    { $eq: ['$$invoices.name', 'shipment_invoice'] },
                    {
                      $or: [
                        { $eq: [req['user']['_id'], '$company._id'] },
                        { $eq: ['$employee.show_invoice', true] },
                      ],
                    },
                  ],
                },
              },
            },
          },
        },
        {
          $addFields: {
            container_number: {
              $trim: {
                input: {
                  $reduce: {
                    input: '$container_number',
                    initialValue: '',
                    in: { $concat: ['$$value', '$$this', ', '] },
                  },
                },
                chars: ', ',
              },
            },
          },
        },
        {
          $project: {
            current_status_id: 0,
            company_id: 0,
            verification_code: 0,
          },
        },
      ]);
      if (data.length === 0) {
        throw new BadRequestException(
          ErrorMsg?.SHIPMENT_NOT_FOUND[lang || 'EN'],
        );
      }
      return res
        .status(HttpStatus.OK)
        .json({ success: true, statusCode: 200, data: data[0] });
    } catch (error) {
      throw new BadRequestException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************GET SINGLE INQUIRY***********************************/
  async getSingleInquiry(
    req: Request,
    inquiry_id: string,
    lang: string,
    res: Response,
  ) {
    try {
      const data = await this.inquiryModel.aggregate([
        { $match: { _id: new mongoose.Types.ObjectId(inquiry_id) } },
        {
          $lookup: {
            from: 'map_inquiry_attachments',
            as: 'attachments',
            let: { inquiry_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$inquiry_id', '$$inquiry_id'] } } },
              { $sort: { createdAt: -1 } },
              {
                $project: {
                  image: { $concat: [process.env.BASE_URL, '$path'] },
                  uploaded_by: 1,
                  createdAt: 1,
                  original_name: 1,
                },
              },
            ],
          },
        },
        {
          $lookup:{
            from:"quotations",
            localField:"_id",
            foreignField:"inquiry_id",
            as:"quotation"
          }
        },
        {
          $unwind: { path: '$quotation', preserveNullAndEmptyArrays: true },
        },
        {
          $lookup: {
            from: 'map_inquiry_replies',
            as: 'replies',
            let: { inquiry_id: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$inquiry_id', '$$inquiry_id'] } } },
              { $sort: { createdAt: -1 } },
            ],
          },
        },
      ]);
      if (data.length === 0) {
        throw new BadRequestException(
          ErrorMsg?.INQUIRY_NOT_FOUND[lang || 'EN'],
        );
      }
      return res
        .status(HttpStatus.OK)
        .json({ success: true, statusCode: 200, data: data[0] });
    } catch (error) {
      throw new BadRequestException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/

  /*********************************DELETE MY ACCOUNT************************************/
  async deleteMyAccount(req: Request, lang: string, res: Response) {
    try {
      await this.userModel.findByIdAndUpdate(req['user']['_id'], {
        deleted_at: new Date(),
      });
      await this.mapUserDeviceModel.deleteMany({
        user_id: new mongoose.Types.ObjectId(req['user']['_id']),
      });
      return res.status(HttpStatus.CREATED).json({
        success: true,
        message: SuccessMsg?.ACCOUNT_DELETED[lang || EN],
      });
    } catch (error) {
      throw new CustomException(error, error.status);
    }
  }
  /*------------------------------------------------------------------------------------*/
}
