package com.ruoyi.system.service;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.InfoUser;
import com.ruoyi.system.domain.PosFood;
import com.ruoyi.system.domain.PosOrder;
import com.ruoyi.system.domain.PosStore;
import com.ruoyi.system.domain.constants.MerchantAccountConstants;
import com.ruoyi.system.mapper.InfoUserMapper;
import com.ruoyi.system.mapper.MerchantSubaccountStoreMapper;
import com.ruoyi.system.mapper.PosFoodMapper;
import com.ruoyi.system.mapper.PosOrderMapper;
import com.ruoyi.system.mapper.PosStoreMapper;
import org.springframework.stereotype.Service;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 商家主账号和分管账号共用的门店级访问控制。
 */
@Service
public class MerchantStoreAccessService {

    private final InfoUserMapper infoUserMapper;
    private final MerchantSubaccountStoreMapper relationMapper;
    private final PosStoreMapper posStoreMapper;
    private final PosOrderMapper posOrderMapper;
    private final PosFoodMapper posFoodMapper;

    public MerchantStoreAccessService(InfoUserMapper infoUserMapper,
                                      MerchantSubaccountStoreMapper relationMapper,
                                      PosStoreMapper posStoreMapper,
                                      PosOrderMapper posOrderMapper,
                                      PosFoodMapper posFoodMapper) {
        this.infoUserMapper = infoUserMapper;
        this.relationMapper = relationMapper;
        this.posStoreMapper = posStoreMapper;
        this.posOrderMapper = posOrderMapper;
        this.posFoodMapper = posFoodMapper;
    }

    public MerchantAccessContext resolve(Long loginUserId) {
        InfoUser loginUser = requireActiveUser(loginUserId);
        if (MerchantAccountConstants.OWNER_USER_TYPE.equals(loginUser.getUserType())) {
            List<Long> owned = posStoreMapper.selectOwnedStoreIdsByUserId(loginUserId);
            return new MerchantAccessContext(loginUserId, loginUserId, true,
                    owned == null ? Set.of() : new LinkedHashSet<>(owned));
        }
        if (!MerchantAccountConstants.SUBACCOUNT_USER_TYPE.equals(loginUser.getUserType())
                || !MerchantAccountConstants.STATUS_ENABLED.equals(loginUser.getSubaccountStatus())
                || loginUser.getMerchantOwnerId() == null) {
            throw denied("merchant.account.unavailable");
        }

        InfoUser owner = infoUserMapper.selectInfoUserByUserId(loginUser.getMerchantOwnerId());
        if (!isActiveOwner(owner)) {
            throw denied("merchant.owner.unavailable");
        }

        Set<Long> accessible = new LinkedHashSet<>();
        List<Long> authorized = relationMapper.selectStoreIdsBySubaccountUserId(loginUserId);
        if (authorized != null) {
            for (Long storeId : authorized) {
                PosStore store = storeId == null ? null : posStoreMapper.selectPosStoreById(storeId);
                if (store != null
                        && MerchantAccountConstants.NOT_DELETED.equals(store.getDelFlag())
                        && Objects.equals(owner.getUserId(), store.getUserId())) {
                    accessible.add(storeId);
                }
            }
        }
        return new MerchantAccessContext(loginUserId, owner.getUserId(), false, accessible);
    }

    public void requireOwner(Long loginUserId) {
        if (!resolve(loginUserId).owner()) {
            throw denied("merchant.owner.required");
        }
    }

    public Set<Long> getAccessibleStoreIds(Long loginUserId) {
        return resolve(loginUserId).storeIds();
    }

    public void requireStoreAccess(Long loginUserId, Long storeId) {
        if (storeId == null || !resolve(loginUserId).storeIds().contains(storeId)) {
            throw denied("merchant.store.access.denied");
        }
    }

    public PosOrder requireOrderAccess(Long loginUserId, Long orderId) {
        PosOrder order = orderId == null ? null : posOrderMapper.selectPosOrderById(orderId);
        if (order == null) {
            throw denied("merchant.order.not.found");
        }
        requireStoreAccess(loginUserId, order.getMdId());
        return order;
    }

    public PosFood requireFoodAccess(Long loginUserId, Long foodId) {
        PosFood food = foodId == null ? null : posFoodMapper.selectPosFoodById(foodId);
        if (food == null) {
            throw denied("merchant.food.not.found");
        }
        requireStoreAccess(loginUserId, food.getMdid());
        return food;
    }

    private InfoUser requireActiveUser(Long loginUserId) {
        InfoUser user = loginUserId == null ? null : infoUserMapper.selectInfoUserByUserId(loginUserId);
        if (user == null
                || !MerchantAccountConstants.NOT_DELETED.equals(user.getDelFlag())
                || !MerchantAccountConstants.STATUS_ENABLED.equals(user.getStatus())) {
            throw denied("merchant.account.unavailable");
        }
        return user;
    }

    private boolean isActiveOwner(InfoUser owner) {
        return owner != null
                && MerchantAccountConstants.OWNER_USER_TYPE.equals(owner.getUserType())
                && MerchantAccountConstants.STATUS_ENABLED.equals(owner.getStatus())
                && MerchantAccountConstants.NOT_DELETED.equals(owner.getDelFlag());
    }

    private ServiceException denied(String messageKey) {
        return new ServiceException(MessageUtils.message(messageKey));
    }
}
