package tech.easyflow.auth.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.tenant.TenantManager;
import org.springframework.stereotype.Service;
import tech.easyflow.auth.entity.LoginDTO;
import tech.easyflow.auth.entity.LoginVO;
import tech.easyflow.auth.service.AuthService;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysApiKey;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.entity.SysMenu;
import tech.easyflow.system.entity.SysRole;
import tech.easyflow.system.service.SysApiKeyService;
import tech.easyflow.system.service.SysAccountService;
import tech.easyflow.system.service.SysMenuService;
import tech.easyflow.system.service.SysRoleService;
import cn.hutool.crypto.digest.BCrypt;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AuthServiceImpl implements AuthService, StpInterface {

    @Resource
    private SysAccountService sysAccountService;
    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private SysMenuService sysMenuService;
    @Resource
    private SysApiKeyService sysApiKeyService;

    @Override
    public LoginVO login(LoginDTO loginDTO) {
        try {
            TenantManager.ignoreTenantCondition();
            String pwd = loginDTO.getPassword();
            SysAccount record = getAvailableAccount(loginDTO.getAccount(), "用户名/密码错误");
            String pwdDb = record.getPassword();
            if (!BCrypt.checkpw(pwd, pwdDb)) {
                throw new BusinessException("用户名/密码错误");
            }
            return createLoginVO(record);
        } finally {
            TenantManager.restoreTenantCondition();
        }
    }

    @Override
    public LoginVO devLogin(String account) {
        try {
            TenantManager.ignoreTenantCondition();
            SysAccount record = getAvailableAccount(account, "开发免登账号不存在");
            return createLoginVO(record);
        } finally {
            TenantManager.restoreTenantCondition();
        }
    }

    @Override
    public LoginVO loginByApiKey(String apiKey) {
        try {
            TenantManager.ignoreTenantCondition();
            sysApiKeyService.checkApikeyPermission(apiKey, "/public-api/bot/chat");
            SysApiKey sysApiKey = sysApiKeyService.getSysApiKey(apiKey);
            BigInteger accountId = sysApiKey.getCreatedBy();
            if (accountId == null) {
                throw new BusinessException("访问令牌未绑定创建用户");
            }
            Long timeoutSeconds = resolveApiKeyLoginTimeoutSeconds(sysApiKey);
            return loginByAccountId(accountId, timeoutSeconds);
        } finally {
            TenantManager.restoreTenantCondition();
        }
    }

    @Override
    public LoginVO loginByAccountId(BigInteger accountId, Long timeoutSeconds) {
        SysAccount record = getAvailableAccount(accountId, "账号不存在或不可用");
        return createLoginVO(record, timeoutSeconds);
    }

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        List<SysMenu> menus = sysMenuService.getMenusByAccountId(new SysMenu(), BigInteger.valueOf(Long.parseLong(loginId.toString())));
        return menus.stream()
                .map(SysMenu::getPermissionTag)
                .distinct()
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        List<SysRole> roles = sysRoleService.getRolesByAccountId(BigInteger.valueOf(Long.parseLong(loginId.toString())));
        return roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
    }

    private LoginVO createLoginVO(SysAccount record) {
        return createLoginVO(record, null);
    }

    private LoginVO createLoginVO(SysAccount record, Long timeoutSeconds) {
        if (timeoutSeconds != null) {
            SaLoginModel loginModel = new SaLoginModel();
            loginModel.setTimeout(timeoutSeconds);
            StpUtil.login(record.getId(), loginModel);
        } else {
            StpUtil.login(record.getId());
        }
        LoginAccount loginAccount = new LoginAccount();
        BeanUtil.copyProperties(record, loginAccount);
        StpUtil.getSession().set(Constants.LOGIN_USER_KEY, loginAccount);

        LoginVO res = new LoginVO();
        res.setToken(StpUtil.getTokenValue());
        res.setNickname(record.getNickname());
        res.setAvatar(record.getAvatar());
        res.setForceChangePassword(Boolean.TRUE.equals(record.getPasswordResetRequired()));
        return res;
    }

    private Long resolveApiKeyLoginTimeoutSeconds(SysApiKey sysApiKey) {
        Date expiredAt = sysApiKey.getExpiredAt();
        if (expiredAt == null) {
            return null;
        }
        long remainingMs = expiredAt.getTime() - System.currentTimeMillis();
        if (remainingMs <= 0) {
            throw new BusinessException("apiKey 已过期");
        }
        long timeoutSeconds = (remainingMs + 999) / 1000;
        return Math.max(timeoutSeconds, 1L);
    }

    private SysAccount getAvailableAccount(String account, String accountNotFoundMessage) {
        QueryWrapper w = QueryWrapper.create();
        w.eq(SysAccount::getLoginName, account);
        SysAccount record = sysAccountService.getOne(w);
        return validateAvailableAccount(record, accountNotFoundMessage);
    }

    private SysAccount getAvailableAccount(BigInteger accountId, String accountNotFoundMessage) {
        SysAccount record = sysAccountService.getById(accountId);
        return validateAvailableAccount(record, accountNotFoundMessage);
    }

    private SysAccount validateAvailableAccount(SysAccount record, String accountNotFoundMessage) {
        if (record == null) {
            throw new BusinessException(accountNotFoundMessage);
        }
        if (EnumDataStatus.UNAVAILABLE.getCode().equals(record.getStatus())) {
            throw new BusinessException("账号未启用，请联系管理员");
        }
        return record;
    }
}
