@startuml
!theme mono

title Content Service - 클래스 다이어그램 요약 (Clean Architecture)

' ============================================
' 레이어 구조 표시
' ============================================
package "Domain Layer" <<Rectangle>> {
    class Content {
        - id: Long
        - eventId: String
        - images: List<GeneratedImage>
        + addImage(image: GeneratedImage): void
        + getSelectedImages(): List<GeneratedImage>
    }

    class GeneratedImage {
        - id: Long
        - eventId: String
        - style: ImageStyle
        - platform: Platform
        - cdnUrl: String
        + select(): void
        + deselect(): void
    }

    class Job {
        - id: String
        - eventId: String
        - status: Status
        - progress: int
        + start(): void
        + updateProgress(progress: int): void
        + complete(resultMessage: String): void
        + fail(errorMessage: String): void
    }

    enum ImageStyle {
        FANCY
        SIMPLE
        TRENDY
    }

    enum Platform {
        INSTAGRAM
        FACEBOOK
        KAKAO
        BLOG
    }
}

package "Application Layer" <<Rectangle>> {
    package "Use Cases (Input Port)" {
        interface GenerateImagesUseCase {
            + execute(command: ContentCommand.GenerateImages): JobInfo
        }

        interface GetJobStatusUseCase {
            + execute(jobId: String): JobInfo
        }

        interface GetEventContentUseCase {
            + execute(eventId: String): ContentInfo
        }

        interface GetImageListUseCase {
            + execute(eventId: String, style: ImageStyle, platform: Platform): List<ImageInfo>
        }

        interface DeleteImageUseCase {
            + execute(imageId: Long): void
        }

        interface RegenerateImageUseCase {
            + execute(command: ContentCommand.RegenerateImage): JobInfo
        }
    }

    package "Ports (Output Port)" {
        interface ContentReader {
            + findByEventDraftIdWithImages(eventId: String): Optional<Content>
        }

        interface ContentWriter {
            + save(content: Content): Content
            + saveImage(image: GeneratedImage): GeneratedImage
        }

        interface JobReader {
            + getJob(jobId: String): Optional<RedisJobData>
        }

        interface JobWriter {
            + saveJob(jobData: RedisJobData, ttlSeconds: long): void
            + updateJobStatus(jobId: String, status: String, progress: Integer): void
        }

        interface CDNUploader {
            + upload(imageData: byte[], fileName: String): String
        }
    }

    package "Service Implementation" {
        class StableDiffusionImageGenerator implements GenerateImagesUseCase {
            - replicateClient: ReplicateApiClient
            - cdnUploader: CDNUploader
            - jobWriter: JobWriter
            - contentWriter: ContentWriter
            + execute(command: ContentCommand.GenerateImages): JobInfo
        }

        class JobManagementService implements GetJobStatusUseCase {
            - jobReader: JobReader
            + execute(jobId: String): JobInfo
        }

        class GetEventContentService implements GetEventContentUseCase {
            - contentReader: ContentReader
            + execute(eventId: String): ContentInfo
        }

        class GetImageListService implements GetImageListUseCase {
            - imageReader: ImageReader
            + execute(eventId: String, style: ImageStyle, platform: Platform): List<ImageInfo>
        }

        class DeleteImageService implements DeleteImageUseCase {
            - imageWriter: ImageWriter
            + execute(imageId: Long): void
        }

        class RegenerateImageService implements RegenerateImageUseCase {
            - imageReader: ImageReader
            - imageWriter: ImageWriter
            - jobWriter: JobWriter
            + execute(command: ContentCommand.RegenerateImage): JobInfo
        }
    }
}

package "Infrastructure Layer" <<Rectangle>> {
    class RedisGateway implements ContentReader, ContentWriter, JobReader, JobWriter {
        - redisTemplate: RedisTemplate<String, Object>
        - objectMapper: ObjectMapper
        + getAIRecommendation(eventId: String): Optional<Map<String, Object>>
        + saveJob(jobData: RedisJobData, ttlSeconds: long): void
        + getJob(jobId: String): Optional<RedisJobData>
        + save(content: Content): Content
        + saveImage(image: GeneratedImage): GeneratedImage
    }

    class ReplicateApiClient {
        - apiToken: String
        - baseUrl: String
        + createPrediction(request: ReplicateRequest): ReplicateResponse
        + getPrediction(predictionId: String): ReplicateResponse
    }

    class AzureBlobStorageUploader implements CDNUploader {
        - connectionString: String
        - containerName: String
        - circuitBreaker: CircuitBreaker
        + upload(imageData: byte[], fileName: String): String
    }
}

package "Presentation Layer" <<Rectangle>> {
    class ContentController {
        - generateImagesUseCase: GenerateImagesUseCase
        - getJobStatusUseCase: GetJobStatusUseCase
        - getEventContentUseCase: GetEventContentUseCase
        - getImageListUseCase: GetImageListUseCase
        - deleteImageUseCase: DeleteImageUseCase
        - regenerateImageUseCase: RegenerateImageUseCase
        + generateImages(command: ContentCommand.GenerateImages): ResponseEntity<JobInfo>
        + getJobStatus(jobId: String): ResponseEntity<JobInfo>
        + getContentByEventId(eventId: String): ResponseEntity<ContentInfo>
        + deleteImage(imageId: Long): ResponseEntity<Void>
    }
}

' ============================================
' 관계 정의
' ============================================
Content "1" o-- "0..*" GeneratedImage : contains
GeneratedImage --> ImageStyle
GeneratedImage --> Platform

ContentController ..> GenerateImagesUseCase
ContentController ..> GetJobStatusUseCase
ContentController ..> GetEventContentUseCase
ContentController ..> GetImageListUseCase
ContentController ..> DeleteImageUseCase
ContentController ..> RegenerateImageUseCase

StableDiffusionImageGenerator ..> CDNUploader
StableDiffusionImageGenerator ..> JobWriter
StableDiffusionImageGenerator ..> ContentWriter
StableDiffusionImageGenerator --> ReplicateApiClient

JobManagementService ..> JobReader
GetEventContentService ..> ContentReader
GetImageListService ..> "ImageReader\n(extends ContentReader)"
DeleteImageService ..> "ImageWriter\n(extends ContentWriter)"
RegenerateImageService ..> "ImageReader\n(extends ContentReader)"
RegenerateImageService ..> "ImageWriter\n(extends ContentWriter)"
RegenerateImageService ..> JobWriter

RedisGateway ..|> ContentReader
RedisGateway ..|> ContentWriter
RedisGateway ..|> JobReader
RedisGateway ..|> JobWriter
AzureBlobStorageUploader ..|> CDNUploader

' ============================================
' 레이어 의존성 방향
' ============================================
note top of Content : **Domain Layer**\n순수 비즈니스 로직\n외부 의존성 없음
note top of "Use Cases (Input Port)" : **Application Layer**\nUse Case 인터페이스 (Input Port)\n비즈니스 흐름 정의
note top of "Ports (Output Port)" : **Application Layer**\n외부 시스템 추상화 (Output Port)\n의존성 역전 원칙
note top of RedisGateway : **Infrastructure Layer**\nOutput Port 구현체\nRedis 저장소 연동
note top of ContentController : **Presentation Layer**\nREST API 컨트롤러\nHTTP 요청 처리

note bottom of ContentController : **의존성 방향**\nPresentation → Application → Domain\nInfrastructure → Application\n\n**핵심 원칙**\n• Domain은 다른 레이어에 의존하지 않음\n• Application은 Domain에만 의존\n• Infrastructure는 Application Port 구현\n• Presentation은 Application Use Case 호출

@enduml
