package com.app.service.platform.auth;

import com.app.constant.AccountConstant;
import com.app.constant.RedisKeyConstant;
import com.app.dto.PageResult;
import com.app.dto.platform.ResponseAccountDto;
import com.app.dto.platform.SearchAccountDto;
import com.app.entity.BaseEntity;
import com.app.entity.platform.system.*;
import com.app.mapper.platform.*;
import com.app.service.platform.dto.AccountDto;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Service
public class AccountService {

    @Autowired
    private AccountMapper mapper;

    @Autowired
    private AccountAuthsService accountAuthsService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private AccountJobMapper accountJobMapper;

    @Resource
    private AccountDeptMapper accountDeptMapper;

    @Resource
    private AccountRoleMapper accountRoleMapper;

    @Resource
    private AccountAuthsMapper accountAuthsMapper;

    public Optional<Account> findById(String id) {
        return Optional.ofNullable(mapper.selectById(id));
    }

    public AccountDto findCacheById(String id) {
        return (AccountDto) redisTemplate.opsForValue().get(RedisKeyConstant.PlatformAccountLogin(id));
    }

    public Account saveOrUpdate(Account account) {
        Account _account;
        if (StringUtils.isEmpty(account.getId())) {
            _account = new Account();
            _account.setNickname(account.getNickname());
            _account.setAvatarUrl(account.getAvatarUrl());
            _account.setGender(account.getGender());
            mapper.insert(_account);
        } else {
            _account = findById(account.getId()).get();
            _account.setNickname(account.getNickname());
            _account.setAvatarUrl(account.getAvatarUrl());
            _account.setGender(account.getGender());
            mapper.updateById(_account);
        }

        return _account;
    }

    /**
     * 添加或更新账号(账号管理页面中添加/修改)
     */
    @Transactional
    public Account saveOrUpdate(com.app.dto.platform.AccountDto account) {
        Account _account;
        if (StringUtils.isEmpty(account.getId())) {
            _account = new Account();
            _account.setNickname(account.getNickname());
            _account.setIsLocked(account.getIsLocked());
            Date date = account.getEndDateTime();
            if(date!=null){
                Instant instant = date.toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                _account.setEndDateTime(instant.atZone(zoneId).toLocalDateTime());
            }
            _account.setPasswordFailureDateTime(null);
            _account.setType(account.getType());
            _account.setGender(account.getGender());
            mapper.insert(_account);

            AccountAuths auths = new AccountAuths();
            auths.setAccountId(_account.getId());
            auths.setIdentityType(AccountConstant.IdentityType.USERNAME);
            auths.setIdentifier(account.getIdentifier());
            auths.setCredential(account.getCredential());
            accountAuthsService.saveOrUpdate(auths);
            AccountDept dept = new AccountDept();
            dept.setAccountId(_account.getId());
            dept.setDeptId(account.getDeptId());
            accountDeptMapper.insert(dept);
            for (String jobId : account.getJobIds()) {
                AccountJob accountJob = new AccountJob();
                accountJob.setAccountId(_account.getId());
                accountJob.setJobId(jobId);
                accountJobMapper.insert(accountJob);
            }
            for (String roleId : account.getRoleIds()) {
                AccountRole accountRole = new AccountRole();
                accountRole.setAccountId(_account.getId());
                accountRole.setRoleId(roleId);
                accountRoleMapper.insert(accountRole);
            }
        } else {
            _account = findById(account.getId()).get();
            LambdaUpdateWrapper<Account> wrapper = new UpdateWrapper<Account>().lambda();
            wrapper.eq(BaseEntity::getId,_account.getId());
            wrapper.set(Account::getNickname,account.getNickname());
            wrapper.set(Account::getIsLocked,account.getIsLocked());
            wrapper.set(Account::getType,account.getType());
            wrapper.set(Account::getGender,account.getGender());
            Date date = account.getEndDateTime();
            if(date!=null){
                Instant instant = date.toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                wrapper.set(Account::getEndDateTime,instant.atZone(zoneId).toLocalDateTime());
            }else {
                wrapper.set(Account::getEndDateTime,null);
            }
            mapper.update(null,wrapper);

            List<AccountAuths> authsList = accountAuthsService.findByAccountId(account.getId());
            AccountAuths auths;
            if(authsList.isEmpty()){
                auths = new AccountAuths();
                auths.setAccountId(account.getId());
            }else {
                auths = authsList.get(0);
            }
            auths.setIdentityType(AccountConstant.IdentityType.USERNAME);
            auths.setIdentifier(account.getIdentifier());
            auths.setCredential(account.getCredential());
            accountAuthsService.saveOrUpdate(auths);

            AccountDept dept = accountDeptMapper.selectOne(new QueryWrapper<AccountDept>().lambda().eq(AccountDept::getAccountId, account.getId()));
            if(dept == null){
                dept = new AccountDept();
                dept.setAccountId(account.getId());
                dept.setDeptId(account.getDeptId());
                accountDeptMapper.insert(dept);
            }else {
                dept.setDeptId(account.getDeptId());
                accountDeptMapper.updateById(dept);
            }

            accountJobMapper.delete(new QueryWrapper<AccountJob>().lambda().eq(AccountJob::getAccountId,account.getId()));
            for (String jobId : account.getJobIds()) {
                AccountJob accountJob = new AccountJob();
                accountJob.setAccountId(_account.getId());
                accountJob.setJobId(jobId);
                accountJobMapper.insert(accountJob);
            }
            accountRoleMapper.delete(new QueryWrapper<AccountRole>().lambda().eq(AccountRole::getAccountId,account.getId()));
            for (String roleId : account.getRoleIds()) {
                AccountRole accountRole = new AccountRole();
                accountRole.setAccountId(_account.getId());
                accountRole.setRoleId(roleId);
                accountRoleMapper.insert(accountRole);
            }
        }

        return _account;
    }

    public PageResult<ResponseAccountDto> page(SearchAccountDto params) {
        if(params.getIsDel()==null){
            params.setIsDel(false);
        }
        IPage<ResponseAccountDto> page = mapper.page(params,new Page<>(params.getPage(), params.getSize()));
        for (ResponseAccountDto record : page.getRecords()) {
            String accountId = record.getId();
            List<AccountRole> accountRoles = accountRoleMapper.selectList(new QueryWrapper<AccountRole>().lambda().eq(AccountRole::getAccountId, accountId));
            List<String> roleIds = new ArrayList<>();
            for (AccountRole role : accountRoles) {
               roleIds.add(role.getRoleId());
            }
            record.setRoleIds(roleIds);
            List<AccountJob> accountJobs = accountJobMapper.selectList(new QueryWrapper<AccountJob>().lambda().eq(AccountJob::getAccountId, accountId));
            List<String> jobIds = new ArrayList<>();
            for (AccountJob job : accountJobs) {
                jobIds.add(job.getJobId());
            }
            record.setJobIds(jobIds);
        }
        return new PageResult<>(params.getPage(), params.getSize(), page.getTotal(), page.getRecords());
    }

    @Transactional
    public void handleIsDel(String id) {
        Optional<Account> optional = Optional.ofNullable(mapper.selectById(id));
        if (optional.isPresent()) {
            Account account = optional.get();
            String accountId = account.getId();
            accountDeptMapper.delete(new QueryWrapper<AccountDept>().lambda().eq(AccountDept::getAccountId,accountId));
            accountAuthsMapper.delete(new QueryWrapper<AccountAuths>().lambda().eq(AccountAuths::getAccountId,accountId));
            accountJobMapper.delete(new QueryWrapper<AccountJob>().lambda().eq(AccountJob::getAccountId,accountId));
            accountRoleMapper.delete(new QueryWrapper<AccountRole>().lambda().eq(AccountRole::getAccountId,accountId));
            account.setIsDel(!account.getIsDel());
            mapper.updateById(account);
        }
    }
}
