package com.huimv.employment.service.enterprise;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.CreditCodeUtils;
import com.huimv.employment.common.util.MobileUtils;
import com.huimv.employment.dao.entity.FeEnterprise;
import com.huimv.employment.dao.entity.FeEnterpriseUser;
import com.huimv.employment.dao.entity.FeUser;
import com.huimv.employment.dao.mapper.FeEnterpriseMapper;
import com.huimv.employment.dao.mapper.FeEnterpriseUserMapper;
import com.huimv.employment.dao.mapper.FeUserMapper;
import com.huimv.employment.service.config.SecurityProperties;
import com.huimv.employment.service.enterprise.dto.EnterpriseRegisterRequest;
import com.huimv.employment.service.enterprise.dto.EnterpriseRegisterResponse;
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 java.time.LocalDateTime;

/**
 * 企业首次登记：创建企业主体并绑定当前企业主账号。
 */
@Service
public class EnterpriseService {

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

    private static final String USER_TYPE_ENTERPRISE = "enterprise";
    private static final String AUTH_STATUS_PENDING = "pending";
    private static final String ROLE_OWNER = "owner";

    private final FeUserMapper feUserMapper;
    private final FeEnterpriseMapper feEnterpriseMapper;
    private final FeEnterpriseUserMapper feEnterpriseUserMapper;
    private final SecurityProperties securityProperties;

    public EnterpriseService(FeUserMapper feUserMapper,
                             FeEnterpriseMapper feEnterpriseMapper,
                             FeEnterpriseUserMapper feEnterpriseUserMapper,
                             SecurityProperties securityProperties) {
        this.feUserMapper = feUserMapper;
        this.feEnterpriseMapper = feEnterpriseMapper;
        this.feEnterpriseUserMapper = feEnterpriseUserMapper;
        this.securityProperties = securityProperties;
    }

    public FeEnterprise findEnterpriseByUserId(Long userId) {
        FeEnterpriseUser binding = findBindingByUserId(userId);
        if (binding == null) {
            return null;
        }
        return feEnterpriseMapper.selectById(binding.getEnterpriseId());
    }

    /**
     * 按主键查询企业，不存在时抛出业务异常。
     */
    public FeEnterprise requireById(Long enterpriseId) {
        if (enterpriseId == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "enterpriseId 不能为空");
        }
        FeEnterprise enterprise = feEnterpriseMapper.selectById(enterpriseId);
        if (enterprise == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "企业不存在");
        }
        return enterprise;
    }

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

    @Transactional(rollbackFor = Exception.class)
    public EnterpriseRegisterResponse register(Long userId, EnterpriseRegisterRequest request) {
        lockEnterpriseUserForUpdate(userId);
        if (findBindingByUserId(userId) != null) {
            throw new BizException(ErrorCode.ENTERPRISE_ALREADY_REGISTERED);
        }

        String creditCode = request.getCreditCode().trim().toUpperCase();
        CreditCodeUtils.validate(creditCode);
        MobileUtils.validate(request.getContactMobile());
        ensureCreditCodeNotUsed(creditCode);

        String aesKey = securityProperties.getMobileAesKey();
        String contactMobileEnc = AesUtils.encrypt(request.getContactMobile(), aesKey);
        String contactMobileMask = MobileUtils.mask(request.getContactMobile());
        LocalDateTime now = LocalDateTime.now();

        FeEnterprise enterprise = new FeEnterprise();
        enterprise.setName(request.getName().trim());
        enterprise.setCreditCode(creditCode);
        enterprise.setLegalPerson(request.getLegalPerson().trim());
        enterprise.setRegisteredAddress(request.getRegisteredAddress().trim());
        enterprise.setContactName(request.getContactName().trim());
        enterprise.setContactMobileEnc(contactMobileEnc);
        enterprise.setContactMobileMask(contactMobileMask);
        enterprise.setAuthStatus(AUTH_STATUS_PENDING);
        enterprise.setCreateTime(now);
        enterprise.setUpdateTime(now);
        enterprise.setDelFlag(0);
        try {
            feEnterpriseMapper.insert(enterprise);
        } catch (DuplicateKeyException ex) {
            throw handleEnterpriseDuplicate(ex, "insert");
        }

        enterprise.setEnterpriseCode(generateEnterpriseCode(enterprise.getId()));
        try {
            feEnterpriseMapper.updateById(enterprise);
        } catch (DuplicateKeyException ex) {
            throw handleEnterpriseDuplicate(ex, "assign enterpriseCode");
        }

        FeEnterpriseUser binding = new FeEnterpriseUser();
        binding.setEnterpriseId(enterprise.getId());
        binding.setUserId(userId);
        binding.setRole(ROLE_OWNER);
        binding.setIsDefault(true);
        binding.setBindTime(now);
        binding.setCreateTime(now);
        binding.setUpdateTime(now);
        binding.setDelFlag(0);
        try {
            feEnterpriseUserMapper.insert(binding);
        } catch (DuplicateKeyException ex) {
            throw handleEnterpriseDuplicate(ex, "bind user");
        }

        return toRegisterResponse(enterprise);
    }

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

    private BizException handleEnterpriseDuplicate(DuplicateKeyException ex, String phase) {
        String message = ex.getMessage();
        if (message != null && (message.contains("uk_fe_enterprise_credit_code") || message.contains("credit_code"))) {
            return new BizException(ErrorCode.BAD_REQUEST, "该统一社会信用代码已被登记");
        }
        if (message != null && (message.contains("uk_fe_enterprise_code") || message.contains("enterprise_code"))) {
            log.error("Duplicate enterprise_code during {}, should not happen", phase, ex);
            return new BizException(ErrorCode.INTERNAL_ERROR);
        }
        if (message != null && message.contains("uk_fe_enterprise_user")) {
            return new BizException(ErrorCode.ENTERPRISE_ALREADY_REGISTERED);
        }
        log.warn("Unexpected duplicate key during enterprise register {}: {}", phase, message);
        return new BizException(ErrorCode.ENTERPRISE_ALREADY_REGISTERED);
    }

    private FeEnterpriseUser findBindingByUserId(Long userId) {
        return feEnterpriseUserMapper.selectOne(new LambdaQueryWrapper<FeEnterpriseUser>()
                .eq(FeEnterpriseUser::getUserId, userId)
                .last("LIMIT 1"));
    }

    private void ensureCreditCodeNotUsed(String creditCode) {
        FeEnterprise existing = feEnterpriseMapper.selectOne(new LambdaQueryWrapper<FeEnterprise>()
                .eq(FeEnterprise::getCreditCode, creditCode)
                .last("LIMIT 1"));
        if (existing != null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该统一社会信用代码已被登记");
        }
    }

    private static String generateEnterpriseCode(Long enterpriseId) {
        return String.format("ENT%d%06d", LocalDateTime.now().getYear(), enterpriseId);
    }

    private static EnterpriseRegisterResponse toRegisterResponse(FeEnterprise enterprise) {
        EnterpriseRegisterResponse response = new EnterpriseRegisterResponse();
        response.setEnterpriseId(enterprise.getId());
        response.setEnterpriseCode(enterprise.getEnterpriseCode());
        response.setName(enterprise.getName());
        response.setCreditCode(enterprise.getCreditCode());
        response.setLegalPerson(enterprise.getLegalPerson());
        response.setRegisteredAddress(enterprise.getRegisteredAddress());
        response.setContactName(enterprise.getContactName());
        response.setContactMobileMask(enterprise.getContactMobileMask());
        response.setAuthStatus(enterprise.getAuthStatus());
        return response;
    }
}
