package com.tzld.piaoquan.api.service.contentplatform.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tzld.piaoquan.api.common.enums.ExceptionEnum;
import com.tzld.piaoquan.api.common.enums.contentplatform.AccountStatusEnum;
import com.tzld.piaoquan.api.common.exception.CommonException;
import com.tzld.piaoquan.api.dao.mapper.contentplatform.ContentPlatformAccountMapper;
import com.tzld.piaoquan.api.dao.mapper.contentplatform.ContentPlatformVerifyCodeMapper;
import com.tzld.piaoquan.api.dao.mapper.contentplatform.ext.ContentPlatformAccountMapperExt;
import com.tzld.piaoquan.api.model.param.contentplatform.*;
import com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformAccount;
import com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformAccountExample;
import com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformVerifyCode;
import com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformVerifyCodeExample;
import com.tzld.piaoquan.api.model.vo.contentplatform.AccountLoginVO;
import com.tzld.piaoquan.api.model.vo.contentplatform.AccountVO;
import com.tzld.piaoquan.api.service.contentplatform.ContentPlatformAccountService;
import com.tzld.piaoquan.growth.common.utils.Md5Util;
import com.tzld.piaoquan.growth.common.utils.RedisUtils;
import com.tzld.piaoquan.growth.common.utils.SendShortMessageUtil;
import com.tzld.piaoquan.growth.common.utils.VerificationCodeGenerator;
import com.tzld.piaoquan.growth.common.utils.page.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class ContentPlatformAccountServiceImpl implements ContentPlatformAccountService {

    private static final long TOKEN_EXPIRE_TIME = 7L * 24 * 60 * 60;
    private static final long TOKEN_INDEX_EXPIRE_TIME = TOKEN_EXPIRE_TIME + 24 * 60 * 60L;

    @Autowired
    ContentPlatformAccountMapper accountMapper;
    @Autowired
    ContentPlatformAccountMapperExt accountMapperExt;
    @Autowired
    ContentPlatformVerifyCodeMapper verifyCodeMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public AccountLoginVO login(AccountLoginParam param) {
        ContentPlatformAccount account = getAccountByTelNum(param.getTelNum());
        if (Objects.isNull(account)) {
            throw new CommonException(ExceptionEnum.ACCOUNT_NOT_EXISTS_WRONG);
        }
        if (account.getStatus() == AccountStatusEnum.FORBIDDEN.getVal()) {
            throw new CommonException(ExceptionEnum.ACCOUNT_BANNED);
        }
        Long now = System.currentTimeMillis();
        if (StringUtils.hasText(param.getPassword())) {
            String password = new String(Base64.getDecoder().decode(param.getPassword()));
            String md5Password = Md5Util.encoderByMd5(password);
            if (!account.getPassword().equals(md5Password)) {
                throw new CommonException(ExceptionEnum.LOGIN_PASSWORD_WRONG);
            }
        } else if (StringUtils.hasText(param.getVerifyCode())) {
            ContentPlatformVerifyCodeExample example = new ContentPlatformVerifyCodeExample();
            example.createCriteria().andTelNumEqualTo(param.getTelNum()).andCodeEqualTo(param.getVerifyCode());
            example.setOrderByClause("id desc");
            List<ContentPlatformVerifyCode> verifyCodeList = verifyCodeMapper.selectByExample(example);
            if (CollectionUtils.isEmpty(verifyCodeList)) {
                throw new CommonException(ExceptionEnum.EMAIL_VERIFY_CODE_NOT_EXIST);
            }
            ContentPlatformVerifyCode verifyCode = verifyCodeList.get(0);
            if (verifyCode.getExprieTime() < now) {
                throw new CommonException(ExceptionEnum.EMAIL_VERIFY_CODE_EXPIRED);
            }
            if (verifyCode.getStatus() == 1) {
                throw new CommonException(ExceptionEnum.EMAIL_VERIFY_CODE_USED);
            }
            verifyCode.setStatus(1);
            verifyCodeMapper.updateByPrimaryKeySelective(verifyCode);
        }
        String token = UUID.randomUUID().toString().replace("-", "");
        String signKey = UUID.randomUUID().toString().replace("-", "");

        AccountLoginVO result = new AccountLoginVO();
        BeanUtils.copyProperties(account, result);
        result.setToken(token);
        result.setSignKey(signKey);

        saveTokenToRedis(result, account.getToken(), token);
        // 保存 signKey 到 Redis，与 token 同生命周期（7天）
        String signKeyRedisKey = "sign_key." + token;
        redisUtils.setValueWithExpire(signKeyRedisKey, signKey, TOKEN_EXPIRE_TIME);
        account.setToken(token);
        account.setTokenExpireTimestamp(now + 7 * 24 * 60 * 60 * 1000L);
        accountMapper.updateByPrimaryKeySelective(account);
        return result;
    }

    @Override
    public ContentPlatformAccount getAccountByTelNum(String telNum) {
        ContentPlatformAccountExample example = new ContentPlatformAccountExample();
        example.createCriteria().andTelNumEqualTo(telNum);
        List<ContentPlatformAccount> accountList = accountMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(accountList)) {
            return null;
        }
        return accountList.get(0);
    }

    private void saveTokenToRedis(AccountLoginVO loginInfo, String oldToken, String token) {
        String tokenPrefix = "login.{token}";
        // 允许同一账号多端登录
        String redisKey = tokenPrefix.replace("{token}", token);
        redisUtils.setValueWithExpire(redisKey, JSON.toJSONString(loginInfo), TOKEN_EXPIRE_TIME);
        String tokenListKey = "login.account.token.list." + loginInfo.getTelNum();
        // 账号资料刷新时可能重新保存同一个 token，先移除以避免列表重复。
        redisUtils.listRemove(tokenListKey, token);
        redisUtils.listLeftPush(tokenListKey, token);
        // 索引比 token 多保留一天，保证过期请求仍能定位并清理账号 token 列表。
        String tokenTelKey = "login.token.tel." + token;
        redisUtils.setValueWithExpire(tokenTelKey, loginInfo.getTelNum(), TOKEN_INDEX_EXPIRE_TIME);
    }

    @Override
    public void sendMessageVerifyCode(AccountSendMessageParam param) {
        if (!StringUtils.hasText(param.getTelNum())) {
            throw new CommonException(ExceptionEnum.ACCOUNT_NOT_EXISTS_WRONG);
        }
        ContentPlatformAccount account = getAccountByTelNum(param.getTelNum());
        if (Objects.isNull(account)) {
            throw new CommonException(ExceptionEnum.ACCOUNT_NOT_EXISTS_WRONG);
        }
        String verifyCode = VerificationCodeGenerator.generateVerificationCode(6);
        SendShortMessageUtil.sendVerifyCode(param.getTelNum(), verifyCode);
        ContentPlatformVerifyCode verifyCodePO = new ContentPlatformVerifyCode();
        verifyCodePO.setTelNum(param.getTelNum());
        verifyCodePO.setCode(verifyCode);
        verifyCodePO.setExprieTime(System.currentTimeMillis() + 10 * 60 * 1000L);
        verifyCodeMapper.insertSelective(verifyCodePO);
    }

    @Override
    public Page<AccountVO> pageAccount(AccountListParam param) {
        Page<AccountVO> result = new Page<>(param.getPageNum(), param.getPageSize());
        int offset = (param.getPageNum() - 1) * param.getPageSize();
        int count = accountMapperExt.getAccountCount(param);
        if (count == 0) {
            return result;
        }
        List<ContentPlatformAccount> accountList = accountMapperExt.getAccountList(param, offset, param.getPageSize());
        List<AccountVO> list = buildAccountVOList(accountList);
        result.setTotalSize(count);
        result.setObjs(list);
        return result;
    }

    private List<AccountVO> buildAccountVOList(List<ContentPlatformAccount> accountList) {
        if (CollectionUtils.isEmpty(accountList)) {
            return null;
        }
        List<AccountVO> result = new ArrayList<>();
        for (ContentPlatformAccount account : accountList) {
            AccountVO accountVO = new AccountVO();
            accountVO.setId(account.getId());
            accountVO.setName(account.getName());
            accountVO.setType(account.getType());
            accountVO.setIdentity(account.getIdentity());
            accountVO.setChannel(account.getChannel());
            accountVO.setContactName(account.getContactName());
            accountVO.setTelNum(account.getTelNum());
            accountVO.setPrice(account.getPrice());
            accountVO.setStatus(account.getStatus());
            accountVO.setCreateTimestamp(account.getCreateTimestamp());
            accountVO.setLayerType(account.getLayerType());
            result.add(accountVO);
        }
        return result;
    }

    @Override
    public void accountForbidden(AccountForbiddenParam param) {
        ContentPlatformAccount account = accountMapper.selectByPrimaryKey(param.getId());
        if (Objects.isNull(account)) {
            throw new CommonException(ExceptionEnum.ACCOUNT_NOT_EXISTS_WRONG);
        }
        account.setStatus(AccountStatusEnum.FORBIDDEN.getVal());
        account.setUpdateAccount(param.getOperator());
        account.setUpdateTimestamp(System.currentTimeMillis());
        accountMapper.updateByPrimaryKeySelective(account);
        // 清除token
        String tokenListKey = "login.account.token.list." + account.getTelNum();
        while(true) {
            String token = redisUtils.listRightPop(tokenListKey);
            if (!StringUtils.hasText(token)) {
                return;
            }
            redisUtils.del("login." + token);
            redisUtils.del("login.token.tel." + token);
            redisUtils.del("sign_key." + token);
        }
    }

    @Override
    public void saveAccount(AccountSaveParam param) {
        List<ContentPlatformAccount> telNumExistList = accountMapperExt.getTelNumExists(param.getId(), param.getTelNum());
        if (CollectionUtils.isNotEmpty(telNumExistList)) {
            throw new CommonException(ExceptionEnum.TEL_NUM_EXISTS_WRONG);
        }
        List<ContentPlatformAccount> channelExistsList = accountMapperExt.getChannelExists(param.getId(), param.getChannel());
        if (CollectionUtils.isNotEmpty(channelExistsList)) {
            throw new CommonException(ExceptionEnum.CHANNEL_EXISTS_WRONG);
        }
        if (!StringUtils.hasText(param.getChannel())) {
            throw new CommonException(ExceptionEnum.CHANNEL_NULL_WRONG);
        }
        try {
            JSONObject.parseObject(param.getPrice());
        } catch (Exception e) {
            throw new CommonException(ExceptionEnum.ACCOUNT_PRICE_ERROR);
        }
        if (!StringUtils.hasText(param.getPrice())) {
            throw new CommonException(ExceptionEnum.ACCOUNT_PRICE_NULL_ERROR);
        }
        Long now = System.currentTimeMillis();
        ContentPlatformAccount account = new ContentPlatformAccount();
        account.setName(param.getName());
        account.setType(param.getType());
        account.setIdentity(param.getIdentity());
        account.setChannel(param.getChannel());
        account.setContactName(param.getContactName());
        account.setTelNum(param.getTelNum());
        account.setPrice(param.getPrice());
        account.setLayerType(param.getLayerType());
        if (StringUtils.hasText(param.getPassword())) {
            String password = new String(Base64.getDecoder().decode(param.getPassword()));
            account.setPassword(Md5Util.encoderByMd5(password));
        }
        account.setUpdateAccount(param.getOperator());
        account.setUpdateTimestamp(now);
        if (Objects.isNull(param.getId())) {
            account.setCreateAccount(param.getOperator());
            account.setCreateTimestamp(now);
            accountMapper.insertSelective(account);
        } else {
            account.setId(param.getId());
            accountMapper.updateByPrimaryKeySelective(account);
            reSetToken(param.getId());
        }
    }

    private void reSetToken(Long id) {
        ContentPlatformAccount account = accountMapper.selectByPrimaryKey(id);
        if (Objects.isNull(account) || !StringUtils.hasText(account.getToken())) {
            return;
        }
        AccountLoginVO loginVO = new AccountLoginVO();
        BeanUtils.copyProperties(account, loginVO);
        saveTokenToRedis(loginVO, account.getToken(), account.getToken());
    }


    @Override
    public List<ContentPlatformAccount> getAccountListByChannels(List<String> channels) {
        ContentPlatformAccountExample example = new ContentPlatformAccountExample();
        example.createCriteria().andChannelIn(channels);
        return accountMapper.selectByExample(example);
    }

    @Override
    public ContentPlatformAccount getAccountByChannel(String channel) {
        ContentPlatformAccountExample example = new ContentPlatformAccountExample();
        example.createCriteria().andChannelEqualTo(channel);
        List<ContentPlatformAccount> list = accountMapper.selectByExample(example);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public ContentPlatformAccount getAccountById(Long id) {
        return accountMapper.selectByPrimaryKey(id);
    }
}
