package com.ttstd.signaling;

import com.ttstd.signaling.model.DeviceBinding;
import com.ttstd.signaling.repository.UserAccountRepository;
import com.ttstd.signaling.security.AuthException;
import com.ttstd.signaling.security.SecurityProperties;
import com.ttstd.signaling.service.AuditService;
import com.ttstd.signaling.service.BindingService;
import com.ttstd.signaling.service.PairingService;
import com.ttstd.signaling.service.TurnCredentialService;
import com.ttstd.signaling.store.InMemoryBindingStore;
import com.ttstd.signaling.store.InMemoryBlacklistStore;
import com.ttstd.signaling.store.InMemoryPairingStore;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

class PairingAndTurnTest {

    private PairingService pairingService;
    private BindingService bindingService;

    @BeforeEach
    void setUp() {
        bindingService = new BindingService(
                new InMemoryBindingStore(), new InMemoryBlacklistStore(),
                Mockito.mock(UserAccountRepository.class), new AuditService());
        pairingService = new PairingService(
                new InMemoryPairingStore(), bindingService, new AuditService(), 600);
    }

    @Test
    void generateThenRedeemCreatesBinding() {
        String code = pairingService.generate("dev_p1");
        DeviceBinding binding = pairingService.redeem(code, "usr_p1");
        assertNotNull(binding);
        assertTrue(bindingService.isBound("dev_p1", "usr_p1"));
    }

    @Test
    void redeemIsOneTimeUse() {
        String code = pairingService.generate("dev_p2");
        pairingService.redeem(code, "usr_p2");
        assertThrows(AuthException.class, () -> pairingService.redeem(code, "usr_other"));
    }

    @Test
    void redeemUnknownCodeFails() {
        assertThrows(AuthException.class, () -> pairingService.redeem("ZZZZZZZZ", "usr_x"));
    }

    @Test
    void redeemIsCaseInsensitive() {
        String code = pairingService.generate("dev_p3");
        // 大写后仍能兑换（生成码本身为大写 base32 字符集，这里再确认大小写归一）
        DeviceBinding binding = pairingService.redeem(code.toUpperCase(), "usr_p3");
        assertNotNull(binding);
    }

    @Test
    void turnCredentialsIssuedWhenEnabled() {
        SecurityProperties props = new SecurityProperties();
        props.getTurn().setEnabled(true);
        props.getTurn().setSharedSecret("test-turn-secret");
        props.getTurn().setUrls("turn:turn.ttstd.com:3478?transport=udp");
        props.getTurn().setTtlSeconds(3600);
        TurnCredentialService turn = new TurnCredentialService(props);

        Map<String, Object> creds = turn.issue("controller:usr_1");
        assertTrue(turn.isEnabled());
        List<?> ice = (List<?>) creds.get("iceServers");
        assertNotNull(ice);
        assertEquals(1, ice.size());
        @SuppressWarnings("unchecked")
        Map<String, String> server = (Map<String, String>) ice.get(0);
        assertTrue(server.containsKey("username"));
        assertTrue(server.containsKey("credential"));
        assertTrue(server.get("urls").startsWith("turn:"));
    }

    @Test
    void turnDisabledThrows() {
        SecurityProperties props = new SecurityProperties();
        props.getTurn().setEnabled(false);
        TurnCredentialService turn = new TurnCredentialService(props);
        assertFalse(turn.isEnabled());
        assertThrows(AuthException.class, () -> turn.issue("scope"));
    }
}
