package com.xunhong.erp.turbo.user.infrastructure.gateway;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xunhong.erp.turbo.api.facade.dto.qry.WxMaOpenidQry;
import com.xunhong.erp.turbo.api.user.dto.cmd.*;
import com.xunhong.erp.turbo.api.user.dto.enums.UserStateEnum;
import com.xunhong.erp.turbo.api.user.dto.qry.UserListQry;
import com.xunhong.erp.turbo.api.user.dto.qry.UserPageQry;
import com.xunhong.erp.turbo.api.user.dto.qry.UserShowQry;
import com.xunhong.erp.turbo.datasource.util.AesUtil;
import com.xunhong.erp.turbo.user.domain.entity.User;
import com.xunhong.erp.turbo.user.domain.gateway.UserGateway;
import com.xunhong.erp.turbo.user.infrastructure.convert.UserConvert;
import com.xunhong.erp.turbo.user.infrastructure.entity.UserDO;
import com.xunhong.erp.turbo.user.infrastructure.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class UserGatewayImpl implements UserGateway {
	private final UserMapper userMapper;

	private final UserConvert userConvert;

	private final IdentifierGenerator identifierGenerator;

	@Override
	public User create(UserCreateCmd userCreateCmd) {
		Long userId = identifierGenerator.nextId(UserDO.class).longValue();

		// 创建用户
		UserDO userDO = new UserDO();

		String phone = userCreateCmd.getPhone();

		userDO.setPhone(phone);

		userDO.setUserId(userId);
		userDO.setIsAdmin(Boolean.FALSE);
		userDO.setState(UserStateEnum.ENABLE);
		userDO.setRemark(userCreateCmd.getRemark());
		userDO.setBirthday(userCreateCmd.getBirthday());
		userDO.setGender(userCreateCmd.getGender());
		userMapper.insert(userDO);

		return userConvert.toUser(userDO);
	}

	@Override
	public User show(UserShowQry userShowQry) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getUserId, userShowQry.getUserId());

		UserDO userDO = userMapper.selectOne(queryWrapper);

		return userConvert.toUser(userDO);
	}

	@Override
	public void destroy(UserDestroyCmd userDestroyCmd) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getUserId, userDestroyCmd.getUserId());

		UserDO userDO = userMapper.selectOne(queryWrapper);

		// userDO 不存在
		if (Objects.isNull(userDO)) {
			throw new BizException("400", "用户不存在");
		}

		userDO.deleteById();
	}

	@Override
	public User getByUserId(Long userId) {
		UserDO userDO = userMapper.selectById(userId);

		return userConvert.toUser(userDO);
	}

	@Override
	@Transactional
	public User updateUser(UserUpdateCmd userUpdateCmd) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getUserId, userUpdateCmd.getUserId());
		queryWrapper.select(UserDO::getUserId);

		UserDO userDO = userMapper.selectOne(queryWrapper);
		userConvert.toUser(userDO, userUpdateCmd);

		// 生日只能设置一次
		if (Objects.nonNull(userUpdateCmd.getBirthday()) && Objects.isNull(userDO.getBirthday())) {
			userDO.setBirthday(userUpdateCmd.getBirthday());
		}

		// 手机号码只在不存在的时候更新
		if (Objects.nonNull(userUpdateCmd.getPhone()) && Objects.isNull(userDO.getPhone())) {
			userDO.setPhone(userUpdateCmd.getPhone());
			userDO.setBindPhoneAt(LocalDateTime.now());
		}

		userDO.updateById();

		return userConvert.toUser(userDO);
	}

	@Override
	public IPage<User> page(UserPageQry userPageQry) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(Objects.nonNull(userPageQry.getIsAdmin()), UserDO::getIsAdmin, userPageQry.getIsAdmin());
		if (!StrUtil.isBlankIfStr(userPageQry.getKeyword())) {
			queryWrapper.and(userDOLambdaQueryWrapper -> userDOLambdaQueryWrapper.like(UserDO::getNickname, userPageQry.getKeyword())
				.or().apply("u.name LIKE {0}", "%".concat(userPageQry.getKeyword()).concat("%")));
		}
		if (!StrUtil.isBlankIfStr(userPageQry.getName())) {
			queryWrapper.apply("u.name LIKE {0}", "%".concat(userPageQry.getName()).concat("%"));
		}
		queryWrapper.like(!StrUtil.isBlankIfStr(userPageQry.getNickname()), UserDO::getNickname, userPageQry.getNickname());
		queryWrapper.eq(!StrUtil.isBlankIfStr(userPageQry.getPhone()), UserDO::getPhone, AesUtil.encrypt(userPageQry.getPhone()));
		queryWrapper.in(Objects.nonNull(userPageQry.getState()), UserDO::getState, userPageQry.getState());
		queryWrapper.eq(Objects.nonNull(userPageQry.getGender()), UserDO::getGender, userPageQry.getGender());
		queryWrapper.like(!StrUtil.isBlankIfStr(userPageQry.getRemark()), UserDO::getRemark, userPageQry.getRemark());
		if (CollUtil.isNotEmpty(userPageQry.getCreatedAt())) {
			queryWrapper.apply("u.created_at between {0} and {1}", userPageQry.getCreatedAt().get(0), userPageQry.getCreatedAt().get(1));
		}
		if (Objects.nonNull(userPageQry.getBindPhoneAt())) {
			queryWrapper.between(UserDO::getBindPhoneAt, userPageQry.getBindPhoneAt().get(0), userPageQry.getBindPhoneAt().get(1));
		}

		if (Objects.nonNull(userPageQry.getCommunityId())) {
			queryWrapper.apply("u.user_id in (select user_id from user_community where community_id = {0})", userPageQry.getCommunityId());
		}

		IPage<UserDO> page = new Page<>(userPageQry.getPageIndex(), userPageQry.getPageSize());
		page = userMapper.selectPage(page, queryWrapper, userPageQry);

		return page.convert(userConvert::toUser);
	}

	@Override
	public void disable(UserDisableCmd userDisableCmd) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getUserId, userDisableCmd.getUserId());

		UserDO userDO = userMapper.selectOne(queryWrapper);

		LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class);
		updateWrapper.eq(UserDO::getUserId, userDO.getUserId());
		updateWrapper.set(UserDO::getState, UserStateEnum.DISABLE);

		userMapper.update(updateWrapper);
	}

	@Override
	public List<User> list(UserListQry userListQry) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.in(!CollectionUtil.isEmpty(userListQry.getUserIdList()), UserDO::getUserId, userListQry.getUserIdList());
		queryWrapper.eq(!StrUtil.isBlankIfStr(userListQry.getName()), UserDO::getName, userListQry.getName());
		queryWrapper.select(UserDO::getUserId, UserDO::getName, UserDO::getNickname, UserDO::getAvatar, UserDO::getPhone);

		List<UserDO> userDOList = userMapper.selectList(queryWrapper);
		return userDOList.stream().map(userConvert::toUser).toList();
	}

	@Override
	public void enable(UserEnableCmd userEnableCmd) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getUserId, userEnableCmd.getUserId());

		UserDO userDO = userMapper.selectOne(queryWrapper);

		LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class);
		updateWrapper.eq(UserDO::getUserId, userDO.getUserId());
		updateWrapper.set(UserDO::getState, UserStateEnum.ENABLE);

		userMapper.update(updateWrapper);
	}

	@Override
	public void updateWx(UserUpdateWxCmd userUpdateWxCmd) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getUserId, userUpdateWxCmd.getUserId());
		queryWrapper.select(UserDO::getUserId);
		UserDO userDO = userMapper.selectOne(queryWrapper);
		userConvert.toUser(userDO, userUpdateWxCmd);
		userDO.updateById();
	}

	@Override
	public void unsubscribe(WxMaOpenidQry wxMaOpenidQry) {
		LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class);
		queryWrapper.eq(UserDO::getMpOpenid, wxMaOpenidQry.getMpOpenid());
		queryWrapper.select(UserDO::getUserId);
		UserDO userDO = userMapper.selectOne(queryWrapper);

		if (Objects.isNull(userDO)) {
			return;
		}

		LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class);
		updateWrapper.eq(UserDO::getUserId, userDO.getUserId());
		updateWrapper.set(UserDO::getMpOpenid, null);
		userMapper.update(updateWrapper);
	}

	@Override
	public void updateLastLoginAt(Long userId, LocalDateTime lastLoginAt) {
		LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class);
		updateWrapper.eq(UserDO::getUserId, userId);
		updateWrapper.set(UserDO::getLastLoginAt, lastLoginAt);
		userMapper.update(updateWrapper);
	}
}
