package com.storlead.login;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.storlead.framework.auth.jwt.JwtUtil;
import com.storlead.framework.auth.vo.LoginUser;
import com.storlead.framework.common.constant.CommonConstant;
import com.storlead.framework.common.constant.RedisKeySaltConstant;
import com.storlead.framework.common.ecode.BCryptPasswordEncoder;
import com.storlead.framework.common.result.Result;
import com.storlead.framework.common.util.RsaUtils;
import com.storlead.framework.core.tenant.TenantContext;
import com.storlead.framework.redis.RedisService;
import com.storlead.sms.spi.SmsCaptchaScene;
import com.storlead.sms.spi.SmsCaptchaService;
import com.storlead.system.service.IMenuService;
import com.storlead.system.service.IUserRoleService;
import com.storlead.system.util.SystemConfigItemCacheUtil;
import com.storlead.user.pojo.dto.TenantLoginDTO;
import com.storlead.user.pojo.entity.DeptEntity;
import com.storlead.user.pojo.entity.JobEntity;
import com.storlead.user.pojo.entity.TenantEnterpriseEntity;
import com.storlead.user.pojo.entity.TenantUserEntity;
import com.storlead.user.pojo.entity.UserEntity;
import com.storlead.user.service.IDepartService;
import com.storlead.user.service.IJobService;
import com.storlead.user.service.ITenantEnterpriseService;
import com.storlead.user.service.ITenantUserService;
import com.storlead.user.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 租户空间登录：先用 {@code user} 表完成账号校验，再以表 {@code tenant_user} 判定是否属于目标租户，并写入 {@link LoginUser#setTenantId}。
 */
@RestController
@RequestMapping("/sys/auth")
@Api(tags = "租户: 租户登录")
@Slf4j
public class TenantLoginApiController {

    @Resource
    private RedisService redisService;
    @Resource
    private IUserService userService;
    @Resource
    private IMenuService menuService;
    @Resource
    private IDepartService departService;
    @Resource
    private SmsCaptchaService smsCaptchaService;
    @Value("${environment}")
    private String environment;
    @Resource
    private IUserRoleService userRoleService;
    @Resource
    private IJobService jobService;
    @Resource
    private ITenantEnterpriseService tenantEnterpriseService;
    @Resource
    private ITenantUserService tenantUserService;

    @RequestMapping(value = "/tenant/login", method = RequestMethod.POST)
    @ApiOperation(value = "租户登录", notes = "仅支持短信验证码或密码；需指定 tenantId 或 enterpriseCode；通过后必须在 tenant_user 存在对应关系")
    public Result<?> tenantLogin(@RequestBody TenantLoginDTO loginDTO) {
        if (loginDTO == null) {
            return Result.error("参数错误");
        }
        if (loginDTO.getTenantId() == null && StrUtil.isBlank(loginDTO.getEnterpriseCode())) {
            return Result.error("请指定 tenantId 或 enterpriseCode");
        }

        TenantContext.setIgnoreTenantLine(true);
        try {
            TenantEnterpriseEntity enterprise;
            if (loginDTO.getTenantId() != null) {
                enterprise = tenantEnterpriseService.getByTenantId(loginDTO.getTenantId());
            } else {
                enterprise = tenantEnterpriseService.getByEnterpriseCode(loginDTO.getEnterpriseCode());
            }
            if (enterprise == null) {
                return Result.error("租户不存在或已删除");
            }
            if (Boolean.FALSE.equals(enterprise.getEnabled())) {
                return Result.error("租户已禁用");
            }
            if (enterprise.getExpireAt() != null && enterprise.getExpireAt().before(new Date())) {
                return Result.error("租户已过期");
            }

            JSONObject obj = new JSONObject();
            UserEntity userInfo = authenticateAccount(loginDTO, obj);
            if (userInfo == null) {
                return Result.error(obj.getString("_err"));
            }

            if (!userInfo.getEnabled()) {
                return Result.error("该账号用户暂无登录权限");
            }

            // 租户成员以 tenant_user 为准：无记录则不允许进入该租户空间
            TenantUserEntity tenantUser = tenantUserService.getByTenantIdAndUserId(enterprise.getTenantId(), userInfo.getId());
            if (tenantUser == null) {
                return Result.error("该账号未在 tenant_user 中绑定当前租户");
            }
            if (tenantUser.getEnterpriseId() != null && !tenantUser.getEnterpriseId().equals(enterprise.getId())) {
                return Result.error("租户成员数据不一致，请联系管理员检查 tenant_user");
            }

//            Integer possessMenuCount = menuService.checkUserMenuAccess(userInfo.getId());
//            if (possessMenuCount == 0) {
//                return Result.error("暂未获取到菜单权限,请联系管理员分配权限!");
//            }

            LoginUser loginUser = new LoginUser();
            BeanUtils.copyProperties(userInfo, loginUser);
            loginUser.setTenantId(enterprise.getTenantId());
            loginUser.setIsAdmin(tenantUser.getIsAdmin() != null && tenantUser.getIsAdmin() == 1);

            if (Objects.nonNull(userInfo.getJobId())) {
                JobEntity job = jobService.getById(userInfo.getJobId());
                if (Objects.nonNull(job)) {
                    loginUser.setJobName(job.getName());
                }
            }
            loginUser.setRoleIds(userRoleService.selectUserRoleByUserId(userInfo.getId()));
            if (Objects.nonNull(userInfo.getDeptId())) {
                DeptEntity dept = departService.getById(userInfo.getDeptId());
                if (Objects.nonNull(dept)) {
                    loginUser.setDeptName(dept.getName());
                    loginUser.setDeptJobDes(dept.getDeptJobDes());
                }
            }

            String jwtToken = JwtUtil.createJWT(JSONObject.toJSONString(loginUser), loginUser.getMobile());
            String token = SecureUtil.md5(jwtToken);
            String json = JSONObject.toJSONString(loginUser);
            redisService.setCacheObject(token, json, 60 * 60 * 24L * 7, TimeUnit.SECONDS);

            Map<String, String> apiMap = new HashMap<>();
            redisService.setCacheObject(RedisKeySaltConstant.API_CODE_REDIS + loginUser.getMobile(), apiMap);

            obj.remove("_err");
            obj.put("token", token);
            obj.put("tenantId", enterprise.getTenantId());
            obj.put("enterpriseId", enterprise.getId());
            @SuppressWarnings("unchecked")
            Map<String, Object> body = JSONObject.parseObject(obj.toJSONString(), Map.class);
            return Result.result(body);
        } finally {
            TenantContext.setIgnoreTenantLine(false);
        }
    }

    /**
     * @param out 用于回传密码强度状态或错误信息（_err）
     * @return 已认证用户；失败时返回 null
     */
    private UserEntity authenticateAccount(TenantLoginDTO loginDTO, JSONObject out) {
        try {
            if (StrUtil.isNotBlank(loginDTO.getSmsCode())) {
                String mobile = StrUtil.trimToEmpty(loginDTO.getAccount());
                String smsCode = StrUtil.trimToEmpty(loginDTO.getSmsCode());
                if (StrUtil.isBlank(mobile)) {
                    out.put("_err", "请输入手机号");
                    return null;
                }
                if (StrUtil.isBlank(smsCode)) {
                    out.put("_err", "请输入验证码");
                    return null;
                }
                Result<?> vr = smsCaptchaService.verifyCaptcha(mobile, smsCode, SmsCaptchaScene.LOGIN, true);
                if (!vr.isSuccess()) {
                    out.put("_err", vr.getMessage());
                    return null;
                }
                UserEntity u = userService.getOne(new LambdaQueryWrapper<UserEntity>()
                        .eq(UserEntity::getMobile, mobile)
                        .eq(UserEntity::getIsDelete, CommonConstant.DEL_FLAG_0));
                if (u == null) {
                    out.put("_err", "该手机号未绑定用户或账号已删除");
                }
                return u;
            }
            if (StrUtil.isBlank(loginDTO.getAccount()) || StrUtil.isBlank(loginDTO.getPassword())) {
                out.put("_err", "请使用短信验证码登录（填写 account + smsCode）或密码登录（填写 account + password）");
                return null;
            }
            UserEntity userInfo = userService.getOne(new LambdaQueryWrapper<UserEntity>()
                    .eq(UserEntity::getMobile, loginDTO.getAccount())
                    .eq(UserEntity::getIsDelete, CommonConstant.DEL_FLAG_0));
            if (userInfo == null) {
                out.put("_err", "账号不存在");
                return null;
            }
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            String password = "";
            if ((!environment.equals("prod") && !loginDTO.getPassword().equals("qq123456")) || environment.equals("prod")) {
                try {
                    password = RsaUtils.decryptByPrivateKey(RsaUtils.PRIVATE_KEY, loginDTO.getPassword());
                } catch (Exception e) {
                    out.put("_err", "用户名或密码错误");
                    return null;
                }
            }
            if (environment.equals("prod") || (environment.equals("test")
                    && !loginDTO.getPassword().equals("qq123456")
                    && !password.equals("qq123456"))) {
                if (!encoder.matches(password, userInfo.getPassword())) {
                    out.put("_err", "用户名或密码错误");
                    return null;
                }
            }
            String regex = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{8,}$";
            String defaultPassWord = SystemConfigItemCacheUtil.getDefaultPassWord();
            if (password != null && (!password.matches(regex) || password.equals(defaultPassWord))) {
                out.put("status", 1);
            } else {
                out.put("status", 2);
            }
            return userInfo;
        } catch (Exception e) {
            log.warn("tenant login authenticate failed", e);
            out.put("_err", "登录失败");
            return null;
        }
    }
}
