package com.ruoyi.app.user;

import com.ruoyi.app.user.dto.MerchantSubaccountCreateRequest;
import com.ruoyi.app.user.dto.MerchantSubaccountPasswordRequest;
import com.ruoyi.app.user.dto.MerchantSubaccountStatusRequest;
import com.ruoyi.app.user.dto.MerchantSubaccountStoreView;
import com.ruoyi.app.user.dto.MerchantSubaccountUpdateRequest;
import com.ruoyi.app.user.dto.MerchantSubaccountView;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.InfoUser;
import com.ruoyi.system.domain.PosStore;
import com.ruoyi.system.domain.constants.MerchantAccountConstants;
import com.ruoyi.system.mapper.PosStoreMapper;
import com.ruoyi.system.service.IInfoUserService;
import com.ruoyi.system.service.IMerchantSubaccountStoreService;
import com.ruoyi.system.service.MerchantStoreAccessService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class MerchantSubaccountApplicationService {

    private final IInfoUserService infoUserService;
    private final IMerchantSubaccountStoreService relationService;
    private final MerchantStoreAccessService accessService;
    private final PosStoreMapper posStoreMapper;
    private final MerchantTokenSessionService tokenSessionService;

    public MerchantSubaccountApplicationService(IInfoUserService infoUserService,
                                                IMerchantSubaccountStoreService relationService,
                                                MerchantStoreAccessService accessService,
                                                PosStoreMapper posStoreMapper,
                                                MerchantTokenSessionService tokenSessionService) {
        this.infoUserService = infoUserService;
        this.relationService = relationService;
        this.accessService = accessService;
        this.posStoreMapper = posStoreMapper;
        this.tokenSessionService = tokenSessionService;
    }

    public List<MerchantSubaccountView> listForOwner(Long ownerUserId) {
        accessService.requireOwner(ownerUserId);
        return listOwnedSubaccounts(ownerUserId);
    }

    public List<MerchantSubaccountView> listForPlatform(Long ownerUserId) {
        InfoUser owner = infoUserService.selectInfoUserByUserId(ownerUserId);
        if (owner == null || !MerchantAccountConstants.OWNER_USER_TYPE.equals(owner.getUserType())
                || !MerchantAccountConstants.NOT_DELETED.equals(owner.getDelFlag())) {
            throw error("merchant.owner.not.found");
        }
        return listOwnedSubaccounts(ownerUserId);
    }

    @Transactional(rollbackFor = Exception.class)
    public MerchantSubaccountView create(Long ownerUserId, MerchantSubaccountCreateRequest request) {
        accessService.requireOwner(ownerUserId);
        if (request == null) {
            throw error("merchant.subaccount.request.required");
        }
        String phone = normalizePhone(request.getPhone());
        String name = normalizeRequired(request.getName(), "merchant.subaccount.name.required");
        String password = normalizeRequired(request.getPassword(), "merchant.subaccount.password.required");
        List<Long> storeIds = validateStores(ownerUserId, request.getStoreIds());
        if (infoUserService.getinfouserName(phone) != null || infoUserService.getinfoPhone(phone) != null) {
            throw error("merchant.subaccount.phone.exists");
        }

        InfoUser user = new InfoUser();
        user.setUserName(phone);
        user.setPhone(phone);
        user.setNickName(name);
        user.setPassword(password);
        user.setUserType(MerchantAccountConstants.SUBACCOUNT_USER_TYPE);
        user.setMerchantOwnerId(ownerUserId);
        user.setStatus(MerchantAccountConstants.STATUS_ENABLED);
        user.setSubaccountStatus(MerchantAccountConstants.STATUS_ENABLED);
        user.setAuditStatus("1");
        user.setDelFlag(MerchantAccountConstants.NOT_DELETED);
        if (infoUserService.insertInfoUser(user) != 1 || user.getUserId() == null) {
            throw error("merchant.subaccount.create.failed");
        }
        relationService.replaceRelations(user.getUserId(), storeIds);
        return toView(user, ownerUserId);
    }

    @Transactional(rollbackFor = Exception.class)
    public MerchantSubaccountView update(Long ownerUserId, Long subaccountUserId,
                                         MerchantSubaccountUpdateRequest request) {
        accessService.requireOwner(ownerUserId);
        InfoUser target = requireOwnedSubaccount(ownerUserId, subaccountUserId);
        if (request == null) {
            throw error("merchant.subaccount.request.required");
        }
        String name = normalizeRequired(request.getName(), "merchant.subaccount.name.required");
        List<Long> storeIds = validateStores(ownerUserId, request.getStoreIds());

        InfoUser update = new InfoUser();
        update.setUserId(target.getUserId());
        update.setNickName(name);
        if (infoUserService.updateInfoUser(update) != 1) {
            throw error("merchant.subaccount.update.failed");
        }
        relationService.replaceRelations(target.getUserId(), storeIds);
        target.setNickName(name);
        return toView(target, ownerUserId);
    }

    public void updatePassword(Long ownerUserId, Long subaccountUserId,
                               MerchantSubaccountPasswordRequest request) {
        accessService.requireOwner(ownerUserId);
        InfoUser target = requireOwnedSubaccount(ownerUserId, subaccountUserId);
        String password = normalizeRequired(request == null ? null : request.getPassword(),
                "merchant.subaccount.password.required");
        InfoUser update = new InfoUser();
        update.setUserId(target.getUserId());
        update.setPassword(password);
        if (infoUserService.updateInfoUser(update) != 1) {
            throw error("merchant.subaccount.update.failed");
        }
        tokenSessionService.revokeAll(target.getUserId());
    }

    public void updateOwnerStatus(Long ownerUserId, Long subaccountUserId,
                                  MerchantSubaccountStatusRequest request) {
        accessService.requireOwner(ownerUserId);
        InfoUser target = requireOwnedSubaccount(ownerUserId, subaccountUserId);
        boolean enabled = requireEnabled(request);
        InfoUser update = new InfoUser();
        update.setUserId(target.getUserId());
        update.setSubaccountStatus(enabled
                ? MerchantAccountConstants.STATUS_ENABLED : MerchantAccountConstants.STATUS_DISABLED);
        if (infoUserService.updateInfoUser(update) != 1) {
            throw error("merchant.subaccount.update.failed");
        }
        if (!enabled) {
            tokenSessionService.revokeAll(target.getUserId());
        }
    }

    public void updatePlatformStatus(Long subaccountUserId, MerchantSubaccountStatusRequest request) {
        InfoUser target = requireSubaccount(subaccountUserId);
        boolean enabled = requireEnabled(request);
        InfoUser update = new InfoUser();
        update.setUserId(target.getUserId());
        update.setStatus(enabled
                ? MerchantAccountConstants.STATUS_ENABLED : MerchantAccountConstants.STATUS_DISABLED);
        if (infoUserService.updateInfoUser(update) != 1) {
            throw error("merchant.subaccount.update.failed");
        }
        if (!enabled) {
            tokenSessionService.revokeAll(target.getUserId());
        }
    }

    private List<MerchantSubaccountView> listOwnedSubaccounts(Long ownerUserId) {
        InfoUser filter = new InfoUser();
        filter.setUserType(MerchantAccountConstants.SUBACCOUNT_USER_TYPE);
        filter.setMerchantOwnerId(ownerUserId);
        filter.setDelFlag(MerchantAccountConstants.NOT_DELETED);
        return infoUserService.selectInfoUserList(filter).stream()
                .map(user -> toView(user, ownerUserId))
                .toList();
    }

    private MerchantSubaccountView toView(InfoUser user, Long ownerUserId) {
        MerchantSubaccountView view = new MerchantSubaccountView();
        view.setUserId(user.getUserId());
        view.setName(firstNonBlank(user.getNickName(), user.getFullName(), user.getUserName()));
        view.setPhone(user.getPhone());
        view.setMerchantOwnerId(ownerUserId);
        view.setOwnerEnabled(MerchantAccountConstants.STATUS_ENABLED.equals(user.getSubaccountStatus()));
        view.setPlatformEnabled(MerchantAccountConstants.STATUS_ENABLED.equals(user.getStatus()));
        view.setOnline(tokenSessionService.isOnline(user.getUserId()));
        view.setLastLoginAt(user.getLastLoginAt());
        view.setCreatedAt(user.getCreatedAt());
        List<MerchantSubaccountStoreView> stores = new ArrayList<>();
        for (Long storeId : relationService.selectStoreIdsBySubaccountUserId(user.getUserId())) {
            PosStore store = posStoreMapper.selectPosStoreById(storeId);
            if (store != null && Objects.equals(ownerUserId, store.getUserId())
                    && MerchantAccountConstants.NOT_DELETED.equals(store.getDelFlag())) {
                stores.add(new MerchantSubaccountStoreView(storeId, store.getPosName()));
            }
        }
        view.setStores(stores);
        return view;
    }

    private List<Long> validateStores(Long ownerUserId, List<Long> requested) {
        if (requested == null || requested.isEmpty()) {
            throw error("merchant.subaccount.store.required");
        }
        Set<Long> storeIds = new LinkedHashSet<>();
        for (Long storeId : requested) {
            if (storeId == null) {
                throw error("merchant.store.access.denied");
            }
            storeIds.add(storeId);
        }
        if (!accessService.getAccessibleStoreIds(ownerUserId).containsAll(storeIds)) {
            throw error("merchant.store.access.denied");
        }
        return List.copyOf(storeIds);
    }

    private InfoUser requireOwnedSubaccount(Long ownerUserId, Long subaccountUserId) {
        InfoUser target = requireSubaccount(subaccountUserId);
        if (!Objects.equals(ownerUserId, target.getMerchantOwnerId())) {
            throw error("merchant.subaccount.access.denied");
        }
        return target;
    }

    private InfoUser requireSubaccount(Long subaccountUserId) {
        InfoUser target = subaccountUserId == null ? null
                : infoUserService.selectInfoUserByUserId(subaccountUserId);
        if (target == null
                || !MerchantAccountConstants.SUBACCOUNT_USER_TYPE.equals(target.getUserType())
                || !MerchantAccountConstants.NOT_DELETED.equals(target.getDelFlag())) {
            throw error("merchant.subaccount.not.found");
        }
        return target;
    }

    private boolean requireEnabled(MerchantSubaccountStatusRequest request) {
        if (request == null || request.getEnabled() == null) {
            throw error("merchant.subaccount.status.required");
        }
        return request.getEnabled();
    }

    private String normalizePhone(String value) {
        String phone = normalizeRequired(value, "merchant.subaccount.phone.required")
                .replaceAll("\\s+", "");
        if (phone.length() > 32) {
            throw error("merchant.subaccount.phone.invalid");
        }
        return phone;
    }

    private String normalizeRequired(String value, String key) {
        if (value == null || value.isBlank()) {
            throw error(key);
        }
        return value.strip();
    }

    private String firstNonBlank(String... values) {
        for (String value : values) {
            if (value != null && !value.isBlank()) {
                return value;
            }
        }
        return "";
    }

    private ServiceException error(String key) {
        return new ServiceException(MessageUtils.message(key));
    }
}
