package tech.easyflow.skill.gitimport;

import org.junit.After;
import org.junit.Test;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

/**
 * {@link SkillGitOperationExecutor} 并发与总超时契约测试。
 */
public class SkillGitOperationExecutorTest {

    private SkillGitOperationExecutor operationExecutor;

    /**
     * 关闭测试中创建的远程操作执行器。
     */
    @After
    public void tearDown() {
        if (operationExecutor != null) {
            operationExecutor.close();
        }
    }

    /**
     * 单实例并发达到上限时快速拒绝额外任务。
     *
     * @throws Exception 线程协作失败
     */
    @Test
    public void rejectsOperationWhenConcurrencyIsExhausted() throws Exception {
        SkillGitImportProperties properties = new SkillGitImportProperties();
        properties.setMaxConcurrentOperations(1);
        operationExecutor = new SkillGitOperationExecutor(properties);
        CountDownLatch started = new CountDownLatch(1);
        CountDownLatch release = new CountDownLatch(1);
        ExecutorService caller = Executors.newSingleThreadExecutor();
        try {
            Future<String> first = caller.submit(() -> operationExecutor.execute(() -> {
                started.countDown();
                release.await();
                return "done";
            }));
            assertTrue(started.await(2, TimeUnit.SECONDS));

            BusinessException exception = assertThrows(BusinessException.class,
                    () -> operationExecutor.execute(() -> "second"));

            assertEquals(429, exception.getHttpStatus());
            release.countDown();
            assertEquals("done", first.get(2, TimeUnit.SECONDS));
        } finally {
            release.countDown();
            caller.shutdownNow();
        }
    }

    /**
     * 超过远程操作总时限时返回网关超时并取消任务。
     */
    @Test
    public void cancelsOperationAfterTotalTimeout() {
        SkillGitImportProperties properties = new SkillGitImportProperties();
        properties.setOperationTimeout(Duration.ofMillis(30));
        operationExecutor = new SkillGitOperationExecutor(properties);

        BusinessException exception = assertThrows(BusinessException.class,
                () -> operationExecutor.execute(() -> {
                    Thread.sleep(5_000L);
                    return "late";
                }));

        assertEquals(504, exception.getHttpStatus());
    }

    /**
     * 任务忽略中断并在超时后返回资源时，执行器负责回收该结果。
     *
     * @throws Exception 等待清理回调失败
     */
    @Test
    public void cleansResultProducedAfterTimeout() throws Exception {
        SkillGitImportProperties properties = new SkillGitImportProperties();
        properties.setOperationTimeout(Duration.ofMillis(30));
        operationExecutor = new SkillGitOperationExecutor(properties);
        CountDownLatch cleaned = new CountDownLatch(1);

        assertThrows(BusinessException.class, () -> operationExecutor.execute(() -> {
            try {
                Thread.sleep(5_000L);
            } catch (InterruptedException ignored) {
                // 模拟底层驱动在取消后仍返回已经创建的资源。
            }
            return "late-resource";
        }, result -> cleaned.countDown()));

        assertTrue(cleaned.await(2, TimeUnit.SECONDS));
    }
}
