package com.ttstd.signaling.service;

import com.ttstd.signaling.model.DeviceBinding;
import com.ttstd.signaling.model.DeviceBlacklistEntry;
import com.ttstd.signaling.repository.UserAccountRepository;
import com.ttstd.signaling.security.AuthException;
import com.ttstd.signaling.security.TokenUtils;
import com.ttstd.signaling.store.BindingStore;
import com.ttstd.signaling.store.BlacklistStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 绑定关系与黑名单服务。
 *
 * <p>职责：
 * <ul>
 *   <li>维护主控端账号与被控端设备的绑定关系（{@link DeviceBinding}）；
 *   <li>维护被控端黑名单（{@link DeviceBlacklistEntry}）；
 *   <li>为信令链路提供 <b>是否允许连接</b> 的判定：仅 ACTIVE 绑定且未被拉黑的主控端可发起 OFFER；
 *   <li>承载被控端自助管理（拉黑/解绑）与主控端自助查询。
 * </ul>
 *
 * <p>判定优先级：黑名单 > 绑定。即即便存在绑定，被拉黑后 OFFER 仍被服务端拒绝。
 */
@Service
public class BindingService {

    private static final Logger logger = LoggerFactory.getLogger(BindingService.class);

    private final BindingStore bindingStore;
    private final BlacklistStore blacklistStore;
    private final UserAccountRepository userAccountRepository;
    private final AuditService auditService;

    public BindingService(BindingStore bindingStore, BlacklistStore blacklistStore,
                          UserAccountRepository userAccountRepository, AuditService auditService) {
        this.bindingStore = bindingStore;
        this.blacklistStore = blacklistStore;
        this.userAccountRepository = userAccountRepository;
        this.auditService = auditService;
    }

    // ==================== 绑定 ====================

    /**
     * 创建或恢复绑定关系。若已存在（含 REVOKED）则复用并更新为 ACTIVE。
     *
     * @return 绑定记录
     */
    public DeviceBinding bind(String deviceUid, String userId, DeviceBinding.BindingRole role,
                              String alias, String boundBy) {
        DeviceBinding existing = bindingStore.findByDeviceUidAndUserId(deviceUid, userId).orElse(null);
        if (existing != null) {
            existing.setStatus(DeviceBinding.BindingStatus.ACTIVE);
            existing.setRole(role);
            if (alias != null) {
                existing.setAlias(alias);
            }
            existing.setBoundBy(boundBy);
            DeviceBinding saved = bindingStore.save(existing);
            logger.info("绑定恢复/更新: device={} user={} role={}", deviceUid, userId, role);
            auditService.recordDevice(deviceUid, AuditService.ACTION_BIND, AuditService.RESULT_SUCCESS,
                    null, "user=" + userId + " role=" + role);
            return saved;
        }
        DeviceBinding binding = new DeviceBinding(
                "bind_" + TokenUtils.randomBase62(20), deviceUid, userId, role, alias, boundBy);
        DeviceBinding saved = bindingStore.save(binding);
        logger.info("绑定建立: device={} user={} binding={} role={}", deviceUid, userId,
                saved.getBindingId(), role);
        auditService.recordDevice(deviceUid, AuditService.ACTION_BIND, AuditService.RESULT_SUCCESS,
                null, "user=" + userId + " role=" + role);
        return saved;
    }

    /** 解绑（软删除：置为 REVOKED）。 */
    public void revokeBinding(String deviceUid, String userId, String actor) {
        DeviceBinding binding = bindingStore.findByDeviceUidAndUserId(deviceUid, userId).orElse(null);
        if (binding == null) {
            return;
        }
        binding.setStatus(DeviceBinding.BindingStatus.REVOKED);
        bindingStore.save(binding);
        logger.info("解绑: device={} user={} by={}", deviceUid, userId, actor);
        auditService.recordDevice(deviceUid, AuditService.ACTION_UNBIND, AuditService.RESULT_SUCCESS,
                null, "user=" + userId + " by=" + actor);
    }

    /** 主控端是否对该设备拥有 ACTIVE 绑定。 */
    public boolean isBound(String deviceUid, String userId) {
        return bindingStore.findByDeviceUidAndUserId(deviceUid, userId)
                .map(DeviceBinding::isActive)
                .orElse(false);
    }

    public List<DeviceBinding> listByUser(String userId) {
        return bindingStore.listByUserId(userId);
    }

    public List<DeviceBinding> listByDevice(String deviceUid) {
        return bindingStore.listByDeviceUid(deviceUid);
    }

    // ==================== 黑名单 ====================

    /** 被控端拉黑某主控端账号。 */
    public DeviceBlacklistEntry addBlacklist(String deviceUid, String blockedUserId, String reason,
                                             String actor) {
        if (blacklistStore.existsByDeviceUidAndBlockedUserId(deviceUid, blockedUserId)) {
            DeviceBlacklistEntry existing = blacklistStore
                    .findByDeviceUidAndBlockedUserId(deviceUid, blockedUserId).orElseThrow();
            existing.setReason(reason);
            return blacklistStore.save(existing);
        }
        DeviceBlacklistEntry entry = new DeviceBlacklistEntry(deviceUid, blockedUserId, reason);
        DeviceBlacklistEntry saved = blacklistStore.save(entry);
        logger.info("拉黑: device={} blockedUser={} by={}", deviceUid, blockedUserId, actor);
        auditService.recordDevice(deviceUid, AuditService.ACTION_BLACKLIST_ADD, AuditService.RESULT_SUCCESS,
                null, "blockedUser=" + blockedUserId + " by=" + actor);
        return saved;
    }

    public void removeBlacklist(String deviceUid, String blockedUserId, String actor) {
        if (!blacklistStore.existsByDeviceUidAndBlockedUserId(deviceUid, blockedUserId)) {
            return;
        }
        blacklistStore.delete(deviceUid, blockedUserId);
        logger.info("移除黑名单: device={} blockedUser={} by={}", deviceUid, blockedUserId, actor);
        auditService.recordDevice(deviceUid, AuditService.ACTION_BLACKLIST_REMOVE,
                AuditService.RESULT_SUCCESS, null, "blockedUser=" + blockedUserId + " by=" + actor);
    }

    /** 该主控端是否被设备拉黑。 */
    public boolean isBlacklisted(String deviceUid, String userId) {
        return blacklistStore.existsByDeviceUidAndBlockedUserId(deviceUid, userId);
    }

    public List<DeviceBlacklistEntry> listBlacklist(String deviceUid) {
        return blacklistStore.listByDeviceUid(deviceUid);
    }

    // ==================== 用户名解析（被控端按用户名拉黑/绑定） ====================

    /**
     * 将用户名解析为用户 ID。失败抛出 {@link AuthException#badRequest}。
     */
    public String resolveUserId(String username) {
        if (username == null || username.isBlank()) {
            throw AuthException.badRequest("用户名不能为空");
        }
        return userAccountRepository.findByUsername(username.trim())
                .map(u -> u.getUserId())
                .orElseThrow(() -> AuthException.badRequest("账号不存在: " + username));
    }
}
