package com.kt.event.eventservice.application.service;

import com.kt.event.common.exception.BusinessException;
import com.kt.event.common.exception.ErrorCode;
import com.kt.event.eventservice.application.dto.response.JobStatusResponse;
import com.kt.event.eventservice.domain.entity.Job;
import com.kt.event.eventservice.domain.enums.JobType;
import com.kt.event.eventservice.domain.repository.JobRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

/**
 * Job 서비스
 *
 * 비동기 작업 상태를 관리합니다.
 *
 * @author Event Service Team
 * @version 1.0.0
 * @since 2025-10-23
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class JobService {

    private final JobRepository jobRepository;

    /**
     * Job 생성
     *
     * @param eventId 이벤트 ID
     * @param jobType 작업 유형
     * @return 생성된 Job
     */
    @Transactional
    public Job createJob(UUID eventId, JobType jobType) {
        log.info("Job 생성 - eventId: {}, jobType: {}", eventId, jobType);

        Job job = Job.builder()
            .eventId(eventId)
            .jobType(jobType)
            .build();

        job = jobRepository.save(job);

        log.info("Job 생성 완료 - jobId: {}", job.getJobId());

        return job;
    }

    /**
     * Job 상태 조회
     *
     * @param jobId Job ID
     * @return Job 상태 응답
     */
    public JobStatusResponse getJobStatus(UUID jobId) {
        log.info("Job 상태 조회 - jobId: {}", jobId);

        Job job = jobRepository.findById(jobId)
            .orElseThrow(() -> new BusinessException(ErrorCode.JOB_001));

        return mapToJobStatusResponse(job);
    }

    /**
     * Job 상태 업데이트
     *
     * @param jobId Job ID
     * @param progress 진행률
     */
    @Transactional
    public void updateJobProgress(UUID jobId, int progress) {
        log.info("Job 진행률 업데이트 - jobId: {}, progress: {}", jobId, progress);

        Job job = jobRepository.findById(jobId)
            .orElseThrow(() -> new BusinessException(ErrorCode.JOB_001));

        job.updateProgress(progress);

        jobRepository.save(job);
    }

    /**
     * Job 완료 처리
     *
     * @param jobId Job ID
     * @param resultKey Redis 결과 키
     */
    @Transactional
    public void completeJob(UUID jobId, String resultKey) {
        log.info("Job 완료 - jobId: {}, resultKey: {}", jobId, resultKey);

        Job job = jobRepository.findById(jobId)
            .orElseThrow(() -> new BusinessException(ErrorCode.JOB_001));

        job.complete(resultKey);

        jobRepository.save(job);

        log.info("Job 완료 처리 완료 - jobId: {}", jobId);
    }

    /**
     * Job 실패 처리
     *
     * @param jobId Job ID
     * @param errorMessage 에러 메시지
     */
    @Transactional
    public void failJob(UUID jobId, String errorMessage) {
        log.info("Job 실패 - jobId: {}, errorMessage: {}", jobId, errorMessage);

        Job job = jobRepository.findById(jobId)
            .orElseThrow(() -> new BusinessException(ErrorCode.JOB_001));

        job.fail(errorMessage);

        jobRepository.save(job);

        log.info("Job 실패 처리 완료 - jobId: {}", jobId);
    }

    // ==== Private Helper Methods ==== //

    /**
     * Job Entity를 JobStatusResponse DTO로 변환
     */
    private JobStatusResponse mapToJobStatusResponse(Job job) {
        return JobStatusResponse.builder()
            .jobId(job.getJobId())
            .jobType(job.getJobType())
            .status(job.getStatus())
            .progress(job.getProgress())
            .resultKey(job.getResultKey())
            .errorMessage(job.getErrorMessage())
            .createdAt(job.getCreatedAt())
            .completedAt(job.getCompletedAt())
            .build();
    }
}
