package com.ycwl.basic.integration.common.service;

import com.ycwl.basic.integration.common.config.IntegrationProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

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

class IntegrationFallbackServiceTest {

    private IntegrationFallbackService service;

    @BeforeEach
    void setUp() {
        service = new IntegrationFallbackService(new IntegrationProperties());
    }

    @Test
    void shouldReturnCachedValueWhenCacheExists() {
        String serviceName = "zt-scenic";
        String cacheKey = "k1";

        // 第一次调用，缓存结果
        service.executeWithFallback(serviceName, cacheKey, () -> "cached", String.class);

        // 第二次调用，应直接返回缓存，不调用远程
        AtomicInteger remoteCalls = new AtomicInteger();
        String result = service.executeWithFallback(serviceName, cacheKey, () -> {
            remoteCalls.incrementAndGet();
            return "remote";
        }, String.class);

        assertEquals("cached", result);
        assertEquals(0, remoteCalls.get());
    }

    @Test
    void shouldCallRemoteWhenNoCacheAndCacheResult() {
        String serviceName = "zt-scenic";
        String cacheKey = "k2";

        AtomicInteger remoteCalls = new AtomicInteger();
        String result = service.executeWithFallback(serviceName, cacheKey, () -> {
            remoteCalls.incrementAndGet();
            return "remote";
        }, String.class);

        assertEquals("remote", result);
        assertEquals(1, remoteCalls.get());
        assertTrue(service.hasFallbackCache(serviceName, cacheKey));
    }

    @Test
    void shouldThrowWhenRemoteFailsAndNoCache() {
        String serviceName = "zt-scenic";
        String cacheKey = "k3";

        RuntimeException ex = assertThrows(RuntimeException.class, () ->
                service.executeWithFallback(serviceName, cacheKey, () -> {
                    throw new RuntimeException("boom");
                }, String.class)
        );

        assertEquals("boom", ex.getMessage());
    }

    @Test
    void shouldClearCache() {
        String serviceName = "zt-scenic";
        String cacheKey = "k4";

        service.executeWithFallback(serviceName, cacheKey, () -> "value", String.class);
        assertTrue(service.hasFallbackCache(serviceName, cacheKey));

        service.clearFallbackCache(serviceName, cacheKey);
        assertFalse(service.hasFallbackCache(serviceName, cacheKey));
    }

    @Test
    void shouldClearAllCache() {
        String serviceName = "zt-scenic";

        service.executeWithFallback(serviceName, "key1", () -> "v1", String.class);
        service.executeWithFallback(serviceName, "key2", () -> "v2", String.class);

        service.clearAllFallbackCache(serviceName);

        assertFalse(service.hasFallbackCache(serviceName, "key1"));
        assertFalse(service.hasFallbackCache(serviceName, "key2"));
    }

    @Test
    void shouldGetStats() {
        String serviceName = "zt-device";

        service.executeWithFallback(serviceName, "d1", () -> "v1", String.class);
        service.executeWithFallback(serviceName, "d2", () -> "v2", String.class);

        IntegrationFallbackService.FallbackCacheStats stats = service.getFallbackCacheStats(serviceName);

        assertEquals(serviceName, stats.getServiceName());
        assertEquals(2, stats.getTotalCacheCount());
        assertEquals(1, stats.getCacheTtlMinutes());
    }

    @Test
    void shouldOnlyCallRemoteOnceWithConcurrentRequests() throws InterruptedException {
        String serviceName = "zt-scenic";
        String cacheKey = "concurrent-test";
        int threadCount = 10;

        AtomicInteger remoteCalls = new AtomicInteger();
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch doneLatch = new CountDownLatch(threadCount);
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);

        // 启动多个线程同时请求
        for (int i = 0; i < threadCount; i++) {
            executor.submit(() -> {
                try {
                    startLatch.await(); // 等待同时开始
                    service.executeWithFallback(serviceName, cacheKey, () -> {
                        remoteCalls.incrementAndGet();
                        try {
                            Thread.sleep(50); // 模拟远程调用耗时
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                        return "result";
                    }, String.class);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    doneLatch.countDown();
                }
            });
        }

        startLatch.countDown(); // 同时放行所有线程
        doneLatch.await(); // 等待所有线程完成
        executor.shutdown();

        // 互斥锁生效：只有一个线程真正调用了远程
        assertEquals(1, remoteCalls.get());
    }
}
