/* eslint-disable no-unused-vars */
const bcrypt = require('bcryptjs')
const { body, validationResult, param } = require('express-validator')
const uid = require('uuid')
const User = require('../models/GPS.User')
const { _sr } = require('../plugins/serverResponses')
const sms = require('../plugins/SMS_Module')
const {
  res500,
  checkValidations,
  removeWhiteSpaces,
  checkMobileNumber,
  normalizeMobileNumber,
  nameOptimizer,
  checkNationalCode,
  isPersian,
  generateRandomDigits
} = require('../plugins/controllersHelperFunctions')
const _faSr = _sr.fa
const { notifyAdmin } = require('../WebSocket/controllers/admin_EventsController')

function sendConfirmationSMS(userId) {
  return new Promise(async (resolve, reject) => {
    try {
      const user = await User.findById(userId)
      if (!user) throw new Error('invalid id')

      const mobileKey = generateRandomDigits(6)
      user.otp = mobileKey
      await user.save()

      await sms.auth.mobileVerificationCode(user.mobile_number, mobileKey, new Date())

      resolve()
    } catch (err) {
      reject(err)
    }
  })
}

module.exports.register_user = [
  [
    body('first_name')
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),

    body('last_name')
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),

    body('national_code')
      .notEmpty()
      .withMessage(_faSr.required.national_code)
      .bail()
      .custom((value, { req }) => {
        if (checkNationalCode(value)) return true
        else return Promise.reject(_faSr.format.national_code)
      })
      .bail()
      .custom((value, { req }) => {
        const nationalCode = value.trim()
        return User.findOne({ national_code: nationalCode }).then(user => {
          if (user && user.national_code === nationalCode)
            return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد'))
          else return true
        })
      }),

    body('province_name').notEmpty().withMessage(_faSr.required.province),

    body('city_name').notEmpty().withMessage(_faSr.required.city),

    body('shopName').notEmpty().withMessage(_faSr.required.address),

    body('mobile_number')
      .notEmpty()
      .withMessage(_faSr.required.phone_number)
      .bail()
      .custom((value, { req }) => {
        const mobileNStatus = checkMobileNumber(value)
        if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number)
        else return Promise.resolve()
      })
      .bail()
      .custom((value, { req }) => {
        const mobileNumber = value.trim()
        return User.findOne({ mobile_number: mobileNumber }).then(user => {
          if (user && user.mobile_number === mobileNumber)
            return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد'))
          else return true
        })
      }),

    body('password')
      .isLength({ min: 4 })
      .withMessage(_faSr.min_char.min4)
      .custom((value, { req }) => {
        if (value === req.body.password_confirmation) return true
        else return Promise.reject(_faSr.response.passwords_not_match)
      })
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const provinceId = uid.v4()
      const cityId = uid.v4()

      const { first_name, last_name, national_code, province_name, city_name, mobile_number, password, shopName } =
        req.body

      const data = {
        last_name,
        province_name: { id: provinceId, provinceName: province_name },
        city_name: { id: cityId, cityName: city_name },
        shopName
      }
      data.first_name = nameOptimizer(first_name)
      data.national_code = national_code.trim()
      data.mobile_number = normalizeMobileNumber(mobile_number)

      // hash user password and done
      const salt = await bcrypt.genSalt(10)
      const hash = await bcrypt.hash(password, salt)
      data.password = hash

      const user = new User(data)
      await user.save()
      notifyAdmin('pendingUsers')

      return res.json({ message: _faSr.response.success_save })
    } catch (err) {
      const registerFailurMsg = 'مشکلی در ثبت نام پیش آمده، لطفا مجددا تلاش کنید'
      res500(res, registerFailurMsg)
    }
  }
]

module.exports.update_user = [
  [
    body('first_name')
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),

    body('last_name')
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),
    body('province_name').notEmpty().withMessage(_faSr.required.province),

    body('city_name').notEmpty().withMessage(_faSr.required.city),

    body('shopName').notEmpty().withMessage(_faSr.required.field),
    body('address').notEmpty().withMessage(_faSr.required.address),

    body('cell_number').notEmpty().withMessage(_faSr.required.address),

    body('birthDate').notEmpty().withMessage(_faSr.required.address)
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const { first_name, last_name, province_name, city_name, shopName, birthDate, address, cell_number } = req.body

      const data = {
        last_name,
        province_name,
        city_name,
        shopName,
        birthDate,
        address,
        cell_number
      }
      if (req.files?.image) {
        const image = req.files?.image
        const fileName = 'profile' + Date.now() + '.' + image.name
        await image.mv(`./static/uploads/images/${fileName}`)
        data.profilePic = `/uploads/images/${fileName}`
      }
      data.first_name = nameOptimizer(first_name)
      await User.findByIdAndUpdate(req.params.id, data)
      const user = await User.findById(req.params.id)
      return res.json({ message: _faSr.response.success_save, user })
    } catch (err) {
      const registerFailurMsg = 'مشکلی در به روز رسانی پیش آمده، لطفا مجددا تلاش کنید'
      throw new Error(registerFailurMsg)
    }
  }
]

module.exports.update_user_password = [
  [
    body('newPassword')
      .isLength({ min: 4 })
      .withMessage(_faSr.min_char.min4)
      .custom((value, { req }) => {
        if (value === req.body.password_confirmation) return true
        else return Promise.reject(_faSr.response.passwords_not_match)
      }),
    body('password').isLength({ min: 4 }).withMessage(_faSr.min_char.min4)
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const { password, newPassword } = req.body
      const { user_id } = req
      const salt = await bcrypt.genSalt(10)
      const hash = await bcrypt.hash(newPassword, salt)
      const user = await User.findById(user_id)
      if (!user) {
        res.status(404)
        throw new Error(_faSr.not_found.user_id)
      }
      const passwordMatch = await bcrypt.compare(password, user.password)
      if (!passwordMatch) {
        res.status(401)
        throw new Error('err')
      }
      user.password = hash
      await user.save()
      return res.json({ message: _faSr.response.success_save })
    } catch (err) {
      throw new Error(err.message)
    }
  }
]

module.exports.send_otp = [
  [body('mobile_number').notEmpty().withMessage(_faSr.required.phone_number)],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const { mobile_number } = req.body
      const user = await User.findOne({ mobile_number })
      if (!user) {
        res.status(404)
        throw new Error(_sr.fa.not_found.user_id)
      }
      await sendConfirmationSMS(user.id)
      res.status(200).json({ msg: 'ok' })
    } catch (err) {
      throw new Error(err.message)
    }
  }
]

module.exports.check_otp = [
  [
    body('mobile_number').notEmpty().withMessage(_faSr.required.phone_number),
    body('otp').notEmpty().withMessage(_faSr.required.phone_number)
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const { mobile_number, otp } = req.body
      const user = await User.findOne({ mobile_number })
      if (!user) throw new Error(_sr.fa.not_found.user_id)
      if (user.otp !== otp) throw new Error('کد اشتباه است')
      if (user.otp === otp) res.status(200).json({ msg: 'ok' })
    } catch (err) {
      throw new Error(err.message)
    }
  }
]

module.exports.resetpass_otp = [
  [
    body('mobile_number').notEmpty().withMessage(_faSr.required.phone_number),
    body('otp').notEmpty().withMessage(_faSr.required.phone_number),
    body('password')
      .isLength({ min: 4 })
      .withMessage(_faSr.min_char.min4)
      .custom((value, { req }) => {
        if (value === req.body?.password_confirmation) return true
        else return Promise.reject(_faSr.response.passwords_not_match)
      })
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const { mobile_number, otp, password } = req.body
      const salt = await bcrypt.genSalt(10)
      const hash = await bcrypt.hash(password, salt)
      const user = await User.findOne({ mobile_number })
      if (!user) throw new Error(_sr.fa.not_found.user_id)
      if (user.otp !== otp) throw new Error('کد اشتباه است')
      if (user.otp === otp) {
        user.password = hash
        user.otp = generateRandomDigits(6)
        user.save()
        res.status(200).json({ msg: _sr.fa.response.success_save })
      }
    } catch (err) {
      throw new Error(err.message)
    }
  }
]

module.exports.getCurrent = async (req, res) => {
  const user = await User.findById(req.user_id).select('-password -token -otp -seenByAdmin')
  if (!user || !user.active) return res.ststus(404).json({ msg: _sr.fa.not_found.user_id })
  else return res.status(200).json({ user })
}

module.exports.getUsersForAdmin = async (req, res) => {
  const user = await User.find({})
    .sort({ active: 1, created_at: -1 })
    .select('-password -token -otp -seenByAdmin -cardBank')
  res.status(200).json(user)
}
module.exports.getPendingUsersForAdmin = async (req, res) => {
  const user = await User.find({ active: false })
    .sort({ active: 1, created_at: -1 })
    .select('-password -token -otp -seenByAdmin -cardBank')
  res.status(200).json(user)
}
module.exports.getUserForAdmin = async (req, res) => {
  const user = await User.findById(req.params.id).select('-password -token -otp -seenByAdmin')
  if (!user) {
    return res.status(404).json({ msg: _sr.fa.not_found.user_id })
  } else {
    user.seenByAdmin = true
    user.save()
    res.status(200).json(user)
  }
}

module.exports.updateUserByAdmin = [
  [
    body('first_name')
      .optional()
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),
    body('last_name')
      .optional()
      .notEmpty()
      .withMessage(_faSr.required.last_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),

    body('national_code')
      .optional()
      .notEmpty()
      .withMessage(_faSr.required.national_code)
      .bail()
      .custom((value, { req }) => {
        if (checkNationalCode(value)) return true
        else return Promise.reject(_faSr.format.national_code)
      })
      .bail()
      .custom((value, { req }) => {
        const nationalCode = value.trim()
        return User.findOne({ national_code: nationalCode, _id: { $ne: req.params.id } }).then(user => {
          if (user && user.national_code === nationalCode)
            return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد'))
          else return true
        })
      }),

    body('mobile_number')
      .optional()
      .notEmpty()
      .withMessage(_faSr.required.phone_number)
      .bail()
      .custom((value, { req }) => {
        const mobileNStatus = checkMobileNumber(value)
        if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number)
        else return Promise.resolve()
      })
      .bail()
      .custom((value, { req }) => {
        const mobileNumber = value.trim()
        return User.findOne({ mobile_number: mobileNumber, _id: { $ne: req.params.id } }).then(user => {
          if (user && user.mobile_number === mobileNumber)
            return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد'))
          else return true
        })
      }),

    body('shopName')
      .optional()
      .custom((value, { req }) => {
        if (value === '') return true
        return value ? true : Promise.reject(_faSr.required.shop_name)
      })
      .bail(),

    body('cell_number')
      .optional()
      .custom((value, { req }) => {
        if (value === '') return true
        return value ? true : Promise.reject(_faSr.required.phone_number)
      })
      .bail(),

    body('birthDate')
      .optional()
      .custom((value, { req }) => {
        if (value === '') return true
        return value ? true : Promise.reject(_faSr.required.birthdate)
      })
      .bail(),

    body('address')
      .optional()
      .custom((value, { req }) => {
        if (value === '') return true
        return value ? true : Promise.reject(_faSr.required.address)
      })
      .bail()
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const { first_name, last_name, national_code, mobile_number, shopName, birthDate, address, cell_number } =
        req.body

      const data = {
        first_name: nameOptimizer(first_name),
        last_name,
        national_code: national_code.trim(),
        mobile_number: normalizeMobileNumber(mobile_number),
        shopName,
        birthDate,
        address,
        cell_number
      }
      // const existMobile = await User.findOne({ mobile_number, _id: { $ne: req.params.id } })
      // if (existMobile) return res.status(400).json({ msg: 'این شماره موبایل قبلا ثبت شده است' })

      const updateUser = await User.findByIdAndUpdate(req.params.id, data, { new: true })
      if (!updateUser) return res.status(404).json({ msg: _sr.fa.not_found.user_id })
      return res.status(200).json({ message: _faSr.response.success_update, user: updateUser })
    } catch (error) {
      const registerFailurMsg = 'مشکلی پیش آمده، لطفا مجددا تلاش کنید'
      res500(res, registerFailurMsg)
    }
  }
]

module.exports.activeUser = [
  [
    param('status').notEmpty().isBoolean().withMessage('این استاتوس اشتباه است'),
    param('id').notEmpty().isMongoId().withMessage('این ایدی اشتباه است')
  ],
  checkValidations(validationResult),
  async (req, res) => {
    const data = await User.findByIdAndUpdate(
      req.params.id,
      {
        active: req.params.status
      },
      { new: true }
    ).select('-password -token -otp -seenByAdmin')
    if (!data) return res.status(404).json({ msg: _sr.fa.not_found.user_id })

    notifyAdmin('pendingUsers')

    res.status(200).json(data)
  }
]

module.exports.addUserByAdmin = [
  [
    body('first_name')
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),

    body('last_name')
      .notEmpty()
      .withMessage(_faSr.required.first_name)
      .bail()
      .isLength({ min: 2 })
      .withMessage(_faSr.min_char.min2)
      .bail()
      .custom((value, { req }) => {
        if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2)
        if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید'))
        else return true
      }),

    body('national_code')
      .notEmpty()
      .withMessage(_faSr.required.national_code)
      .bail()
      .custom((value, { req }) => {
        if (checkNationalCode(value)) return true
        else return Promise.reject(_faSr.format.national_code)
      })
      .bail()
      .custom((value, { req }) => {
        const nationalCode = value.trim()
        return User.findOne({ national_code: nationalCode }).then(user => {
          if (user && user.national_code === nationalCode)
            return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد'))
          else return true
        })
      }),

    body('province_name').notEmpty().withMessage(_faSr.required.province),

    body('city_name').notEmpty().withMessage(_faSr.required.city),

    body('shopName').notEmpty().withMessage(_faSr.required.address),

    body('mobile_number')
      .notEmpty()
      .withMessage(_faSr.required.phone_number)
      .bail()
      .custom((value, { req }) => {
        const mobileNStatus = checkMobileNumber(value)
        if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number)
        else return Promise.resolve()
      })
      .bail()
      .custom((value, { req }) => {
        const mobileNumber = value.trim()
        return User.findOne({ mobile_number: mobileNumber }).then(user => {
          if (user && user.mobile_number === mobileNumber)
            return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد'))
          else return true
        })
      }),

    body('password')
      .isLength({ min: 4 })
      .withMessage(_faSr.min_char.min4)
      .custom((value, { req }) => {
        if (value === req.body.password_confirmation) return true
        else return Promise.reject(_faSr.response.passwords_not_match)
      })
  ],
  checkValidations(validationResult),
  async (req, res) => {
    try {
      const provinceId = uid.v4()
      const cityId = uid.v4()

      const { first_name, last_name, national_code, province_name, city_name, mobile_number, password, shopName } =
        req.body

      const data = {
        last_name,
        province_name: { id: provinceId, provinceName: province_name },
        city_name: { id: cityId, cityName: city_name },
        shopName,
        active: true
      }
      data.first_name = nameOptimizer(first_name)
      data.national_code = national_code.trim()
      data.mobile_number = normalizeMobileNumber(mobile_number)

      // hash user password and done
      const salt = await bcrypt.genSalt(10)
      const hash = await bcrypt.hash(password, salt)
      data.password = hash

      const user = new User(data)
      await user.save()

      return res.status(200).json({ message: _faSr.response.success_save })
    } catch (err) {
      const registerFailurMsg = 'مشکلی در ثبت نام پیش آمده، لطفا مجددا تلاش کنید'
      res500(res, registerFailurMsg)
    }
  }
]
