package tech.easyflow.skill.gitimport;

import jakarta.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * 以有界线程池和总超时执行 Git 远程操作。
 */
@Component
public class SkillGitOperationExecutor {

    private static final Logger LOG = LoggerFactory.getLogger(SkillGitOperationExecutor.class);

    private final SkillGitImportProperties properties;
    private final ThreadPoolExecutor executor;

    /**
     * 创建 Git 远程操作执行器。
     *
     * @param properties Git 导入配置
     */
    public SkillGitOperationExecutor(SkillGitImportProperties properties) {
        this.properties = properties;
        int concurrency = properties.getMaxConcurrentOperations();
        this.executor = new ThreadPoolExecutor(concurrency, concurrency,
                30L, TimeUnit.SECONDS, new SynchronousQueue<>(), threadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        this.executor.allowCoreThreadTimeOut(true);
    }

    /**
     * 在并发与总超时边界内执行远程操作。
     *
     * @param operation 远程操作
     * @param <T> 返回值类型
     * @return 操作结果
     * @throws BusinessException 操作繁忙、超时或执行失败
     */
    public <T> T execute(Callable<T> operation) {
        return execute(operation, ignored -> {
        });
    }

    /**
     * 在并发与总超时边界内执行远程操作，并清理超时后才产出的结果。
     *
     * @param operation 远程操作
     * @param abandonedResultCleanup 超时或中断后结果清理器
     * @param <T> 返回值类型
     * @return 操作结果
     * @throws BusinessException 操作繁忙、超时或执行失败
     */
    public <T> T execute(Callable<T> operation, Consumer<T> abandonedResultCleanup) {
        AtomicBoolean abandoned = new AtomicBoolean();
        AtomicReference<T> produced = new AtomicReference<>();
        Future<T> future;
        try {
            future = executor.submit(() -> {
                T result = operation.call();
                produced.set(result);
                cleanupIfAbandoned(abandoned, produced, abandonedResultCleanup);
                return result;
            });
        } catch (java.util.concurrent.RejectedExecutionException exception) {
            throw new BusinessException(429, 42921, "Git 导入任务繁忙，请稍后重试", exception);
        }
        try {
            T result = future.get(properties.getOperationTimeout().toMillis(), TimeUnit.MILLISECONDS);
            produced.compareAndSet(result, null);
            return result;
        } catch (TimeoutException exception) {
            abandoned.set(true);
            future.cancel(true);
            cleanupIfAbandoned(abandoned, produced, abandonedResultCleanup);
            throw new BusinessException(504, 50421, "Git 仓库读取超时，请稍后重试", exception);
        } catch (InterruptedException exception) {
            abandoned.set(true);
            future.cancel(true);
            cleanupIfAbandoned(abandoned, produced, abandonedResultCleanup);
            Thread.currentThread().interrupt();
            throw new BusinessException(503, 50321, "Git 仓库读取被中断，请稍后重试", exception);
        } catch (ExecutionException exception) {
            Throwable cause = exception.getCause();
            if (cause instanceof RuntimeException runtimeException) {
                throw runtimeException;
            }
            if (cause instanceof Error error) {
                throw error;
            }
            throw new BusinessException(502, 5021, "Git 仓库读取失败，请稍后重试", cause);
        }
    }

    private <T> void cleanupIfAbandoned(AtomicBoolean abandoned,
                                        AtomicReference<T> produced,
                                        Consumer<T> abandonedResultCleanup) {
        if (!abandoned.get()) {
            return;
        }
        T result = produced.getAndSet(null);
        if (result != null) {
            try {
                abandonedResultCleanup.accept(result);
            } catch (RuntimeException exception) {
                LOG.error("清理已放弃的 Git 远程操作结果失败", exception);
            }
        }
    }

    /**
     * 停止执行器并中断尚未结束的远程操作。
     */
    @PreDestroy
    public void close() {
        executor.shutdownNow();
    }

    private ThreadFactory threadFactory() {
        AtomicInteger sequence = new AtomicInteger();
        return task -> {
            Thread thread = new Thread(task, "easyflow-skill-git-" + sequence.incrementAndGet());
            thread.setDaemon(true);
            return thread;
        };
    }
}
