package com.huimv.employment.service.worker;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huimv.employment.common.constant.WorkerConsentType;
import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.common.util.AesUtils;
import com.huimv.employment.common.util.BankAccountUtils;
import com.huimv.employment.common.util.IdCardUtils;
import com.huimv.employment.dao.entity.FeUser;
import com.huimv.employment.dao.entity.FeWorker;
import com.huimv.employment.dao.entity.FeWorkerConsent;
import com.huimv.employment.dao.mapper.FeUserMapper;
import com.huimv.employment.dao.mapper.FeWorkerConsentMapper;
import com.huimv.employment.dao.mapper.FeWorkerMapper;
import com.huimv.employment.service.config.SecurityProperties;
import com.huimv.employment.service.worker.dto.WorkerRegisterRequest;
import com.huimv.employment.service.worker.dto.WorkerRegisterResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;

/**
 * 临时工登记：首次登录后填写实名与银行卡信息。
 */
@Service
public class WorkerService {

    private static final Logger log = LoggerFactory.getLogger(WorkerService.class);

    private static final String USER_TYPE_WORKER = "worker";
    private static final String VERIFY_STATUS_PENDING = "pending";
    private static final String CONSENT_VERSION = "v1.0";

    private final FeUserMapper feUserMapper;
    private final FeWorkerMapper feWorkerMapper;
    private final FeWorkerConsentMapper feWorkerConsentMapper;
    private final SecurityProperties securityProperties;

    public WorkerService(FeUserMapper feUserMapper,
                         FeWorkerMapper feWorkerMapper,
                         FeWorkerConsentMapper feWorkerConsentMapper,
                         SecurityProperties securityProperties) {
        this.feUserMapper = feUserMapper;
        this.feWorkerMapper = feWorkerMapper;
        this.feWorkerConsentMapper = feWorkerConsentMapper;
        this.securityProperties = securityProperties;
    }

    public FeWorker findWorkerByUserId(Long userId) {
        return feWorkerMapper.selectOne(new LambdaQueryWrapper<FeWorker>()
                .eq(FeWorker::getUserId, userId)
                .last("LIMIT 1"));
    }

    public FeUser requireWorkerUser(Long userId) {
        FeUser user = feUserMapper.selectById(userId);
        if (user == null) {
            throw new BizException(ErrorCode.UNAUTHORIZED);
        }
        if (!USER_TYPE_WORKER.equals(user.getUserType())) {
            throw new BizException(ErrorCode.WORKER_ONLY);
        }
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    public WorkerRegisterResponse register(Long userId,
                                           WorkerRegisterRequest request,
                                           String clientIp,
                                           String userAgent) {
        FeUser user = lockWorkerUserForUpdate(userId);
        if (findWorkerByUserId(userId) != null) {
            throw new BizException(ErrorCode.WORKER_ALREADY_REGISTERED);
        }

        String aesKey = securityProperties.getMobileAesKey();
        String idCardNo = request.getIdCardNo().trim().toUpperCase();
        String bankAccount = request.getBankAccount().trim();
        IdCardUtils.validate(idCardNo);
        BankAccountUtils.validate(bankAccount);

        String idCardNoEnc = AesUtils.encrypt(idCardNo, aesKey);
        ensureIdCardNotUsed(idCardNoEnc, userId);

        LocalDateTime now = LocalDateTime.now();
        FeWorker worker = new FeWorker();
        worker.setUserId(userId);
        worker.setRealName(request.getRealName().trim());
        worker.setIdCardNoEnc(idCardNoEnc);
        worker.setIdCardNoMask(IdCardUtils.mask(idCardNo));
        worker.setMobileEnc(user.getMobileEnc());
        worker.setMobileMask(user.getMobileMask());
        worker.setBankAccountEnc(AesUtils.encrypt(bankAccount, aesKey));
        worker.setBankAccountMask(BankAccountUtils.mask(bankAccount));
        worker.setBankName(request.getBankName().trim());
        worker.setBankBranch(StringUtils.hasText(request.getBankBranch()) ? request.getBankBranch().trim() : null);
        worker.setVerifyStatus(VERIFY_STATUS_PENDING);
        worker.setCreateTime(now);
        worker.setUpdateTime(now);
        worker.setDelFlag(0);
        try {
            feWorkerMapper.insert(worker);
        } catch (DuplicateKeyException ex) {
            throw handleWorkerDuplicate(ex, "insert");
        }

        worker.setWorkerNo(generateWorkerNo(worker.getId()));
        try {
            feWorkerMapper.updateById(worker);
        } catch (DuplicateKeyException ex) {
            throw handleWorkerDuplicate(ex, "assign workerNo");
        }

        saveConsent(worker.getId(), WorkerConsentType.LABOR_CONTRACT, "《电子劳动合同》",
                request.getAgreedLaborContract(), now, clientIp, userAgent);
        saveConsent(worker.getId(), WorkerConsentType.PRIVACY_POLICY, "《隐私政策》",
                request.getAgreedPrivacyPolicy(), now, clientIp, userAgent);

        return toRegisterResponse(worker);
    }

    /**
     * 事务内锁定 fe_user 行，串行化同一用户的并发登记请求。
     */
    private FeUser lockWorkerUserForUpdate(Long userId) {
        FeUser user = feUserMapper.selectByIdForUpdate(userId);
        if (user == null) {
            throw new BizException(ErrorCode.UNAUTHORIZED);
        }
        if (!USER_TYPE_WORKER.equals(user.getUserType())) {
            throw new BizException(ErrorCode.WORKER_ONLY);
        }
        return user;
    }

    private BizException handleWorkerDuplicate(DuplicateKeyException ex, String phase) {
        String message = ex.getMessage();
        if (message != null && (message.contains("uk_fe_worker_user_id") || message.contains("user_id"))) {
            return new BizException(ErrorCode.WORKER_ALREADY_REGISTERED);
        }
        if (message != null && (message.contains("uk_fe_worker_worker_no") || message.contains("worker_no"))) {
            log.error("Duplicate worker_no during {}, should not happen", phase, ex);
            return new BizException(ErrorCode.INTERNAL_ERROR);
        }
        log.warn("Unexpected duplicate key during worker register {}: {}", phase, message);
        return new BizException(ErrorCode.WORKER_ALREADY_REGISTERED);
    }

    private void ensureIdCardNotUsed(String idCardNoEnc, Long currentUserId) {
        FeWorker existing = feWorkerMapper.selectOne(new LambdaQueryWrapper<FeWorker>()
                .eq(FeWorker::getIdCardNoEnc, idCardNoEnc)
                .last("LIMIT 1"));
        if (existing != null && !currentUserId.equals(existing.getUserId())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该身份证号已被其他账号登记");
        }
    }

    private void saveConsent(Long workerId,
                             String consentType,
                             String consentTitle,
                             Boolean agreed,
                             LocalDateTime agreedAt,
                             String clientIp,
                             String userAgent) {
        FeWorkerConsent consent = new FeWorkerConsent();
        consent.setWorkerId(workerId);
        consent.setConsentType(consentType);
        consent.setConsentVersion(CONSENT_VERSION);
        consent.setConsentTitle(consentTitle);
        consent.setAgreed(Boolean.TRUE.equals(agreed));
        consent.setAgreedAt(agreedAt);
        consent.setClientIp(clientIp);
        consent.setUserAgent(userAgent);
        consent.setCreateTime(agreedAt);
        feWorkerConsentMapper.insert(consent);
    }

    private static String generateWorkerNo(Long workerId) {
        return String.format("WK%d%06d", LocalDateTime.now().getYear(), workerId);
    }

    private static WorkerRegisterResponse toRegisterResponse(FeWorker worker) {
        WorkerRegisterResponse response = new WorkerRegisterResponse();
        response.setWorkerId(worker.getId());
        response.setWorkerNo(worker.getWorkerNo());
        response.setRealName(worker.getRealName());
        response.setMobileMask(worker.getMobileMask());
        response.setIdCardNoMask(worker.getIdCardNoMask());
        response.setBankAccountMask(worker.getBankAccountMask());
        response.setBankName(worker.getBankName());
        response.setBankBranch(worker.getBankBranch());
        response.setVerifyStatus(worker.getVerifyStatus());
        return response;
    }
}
