package com.kt.distribution.adapter;

import com.kt.distribution.dto.ChannelDistributionResult;
import com.kt.distribution.dto.DistributionRequest;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.retry.annotation.Retry;
import lombok.extern.slf4j.Slf4j;

/**
 * Abstract Channel Adapter
 * 공통 로직 및 Resilience4j 적용
 *
 * @author System Architect
 * @since 2025-10-23
 */
@Slf4j
public abstract class AbstractChannelAdapter implements ChannelAdapter {

    /**
     * 채널로 배포 실행 (Resilience4j 적용)
     *
     * @param request DistributionRequest
     * @return ChannelDistributionResult
     */
    @Override
    @CircuitBreaker(name = "channelApi", fallbackMethod = "fallback")
    @Retry(name = "channelApi")
    @Bulkhead(name = "channelApi")
    public ChannelDistributionResult distribute(DistributionRequest request) {
        long startTime = System.currentTimeMillis();

        try {
            log.info("Starting distribution to channel: {}, eventId: {}",
                    getChannelType(), request.getEventId());

            // 실제 외부 API 호출 (구현체에서 구현)
            ChannelDistributionResult result = executeDistribution(request);
            result.setExecutionTimeMs(System.currentTimeMillis() - startTime);

            log.info("Distribution completed successfully: channel={}, eventId={}, executionTime={}ms",
                    getChannelType(), request.getEventId(), result.getExecutionTimeMs());

            return result;

        } catch (Exception e) {
            long executionTime = System.currentTimeMillis() - startTime;
            log.error("Distribution failed: channel={}, eventId={}, error={}",
                    getChannelType(), request.getEventId(), e.getMessage(), e);

            return ChannelDistributionResult.builder()
                    .channel(getChannelType())
                    .success(false)
                    .errorMessage(e.getMessage())
                    .executionTimeMs(executionTime)
                    .build();
        }
    }

    /**
     * 실제 외부 API 호출 로직 (구현체에서 구현)
     *
     * @param request DistributionRequest
     * @return ChannelDistributionResult
     */
    protected abstract ChannelDistributionResult executeDistribution(DistributionRequest request);

    /**
     * Fallback 메서드 (Circuit Breaker Open 시)
     *
     * @param request DistributionRequest
     * @param throwable Throwable
     * @return ChannelDistributionResult
     */
    protected ChannelDistributionResult fallback(DistributionRequest request, Throwable throwable) {
        log.warn("Fallback triggered for channel: {}, eventId: {}, reason: {}",
                getChannelType(), request.getEventId(), throwable.getMessage());

        return ChannelDistributionResult.builder()
                .channel(getChannelType())
                .success(false)
                .errorMessage("Circuit Breaker Open: " + throwable.getMessage())
                .executionTimeMs(0)
                .build();
    }
}
