openapi: 3.0.3
info:
  title: Content Service API
  version: 1.0.0
  description: |
    # KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API

    ## 주요 기능
    - **SNS 이미지 생성** (UFR-CONT-010): AI 기반 이벤트 이미지 자동 생성
    - **콘텐츠 편집** (UFR-CONT-020): 생성된 이미지 조회, 재생성, 삭제
    - **3가지 스타일**: 심플(SIMPLE), 화려한(FANCY), 트렌디(TRENDY)
    - **3개 플랫폼 최적화**: Instagram (1080x1080), Naver (800x600), Kakao (800x800)
    - **Redis 캐싱**: TTL 7일, 동일 eventDraftId 재요청 시 캐시 반환
    - **CDN 이미지 저장**: Azure Blob Storage 기반

    ## 비동기 처리 아키텍처

    ### Kafka Job Consumer
    **Topic**: `image-generation-job`

    **처리 흐름**:
    1. Kafka에서 이미지 생성 Job 수신 (EventService에서 발행)
    2. Redis에서 AI Service 이벤트 데이터 조회
    3. Redis 캐시에서 기존 이미지 확인 (동일 eventDraftId)
    4. 외부 이미지 생성 API 호출 (Stable Diffusion / DALL-E)
       - **Circuit Breaker**: 5분 타임아웃, 실패율 50% 초과 시 Open
       - **Fallback**: Stable Diffusion → DALL-E → 기본 템플릿 이미지
    5. 생성된 이미지 CDN(Azure Blob) 업로드
    6. Redis에 이미지 URL 저장 (TTL 7일)
    7. Job 상태 업데이트 (PENDING → PROCESSING → COMPLETED/FAILED)

    **Job Payload Schema**: `ImageGenerationJob` (components/schemas 참조)

    ## 외부 API 연동
    - **Image Generation API**: Stable Diffusion / DALL-E
    - **Circuit Breaker**: 5분 타임아웃, 50% 실패율 임계값
    - **CDN**: Azure Blob Storage (이미지 업로드)

    ## 출력 형식
    - 스타일별 3개 × 플랫폼별 3개 = **총 9개 이미지** 생성 (현재는 Instagram만)
    - CDN URL 반환

  contact:
    name: Digital Garage Team
    email: support@kt-event-marketing.com

servers:
  - url: http://localhost:8082
    description: Local Development Server
  - url: https://dev-api.kt-event-marketing.com/content/v1
    description: Development Server
  - url: https://api.kt-event-marketing.com/content/v1
    description: Production Server

tags:
  - name: Job Status
    description: 이미지 생성 작업 상태 조회 (비동기 폴링)
  - name: Content Management
    description: 생성된 콘텐츠 조회 및 관리 (UFR-CONT-020)
  - name: Image Management
    description: 이미지 재생성 및 삭제 (UFR-CONT-020)

paths:
  /api/v1/content/images/generate:
    post:
      tags:
        - Job Status
      summary: SNS 이미지 생성 요청 (비동기)
      description: |
        이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다.

        ## 처리 방식
        - **비동기 처리**: Kafka `image-generation-job` 토픽에 Job 발행
        - **폴링 조회**: jobId로 생성 상태 조회 (GET /api/v1/content/images/jobs/{jobId})
        - **캐싱**: 동일한 eventDraftId 재요청 시 캐시 반환 (TTL 7일)

        ## 생성 스타일
        1. **심플 스타일 (SIMPLE)**: 깔끔한 디자인, 텍스트 중심
        2. **화려한 스타일 (FANCY)**: 눈에 띄는 디자인, 풍부한 색상
        3. **트렌디 스타일 (TRENDY)**: 최신 트렌드, MZ세대 타겟

        ## Resilience 패턴
        - Circuit Breaker (5분 타임아웃, 실패율 50% 초과 시 Open)
        - Fallback (Stable Diffusion → DALL-E → 기본 템플릿)

      operationId: generateImages
      x-user-story: UFR-CONT-010
      x-controller: ImageGenerationController.generateImages
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImageGenerationRequest"
            examples:
              basicEvent:
                summary: 기본 이벤트
                value:
                  eventDraftId: "evt-draft-12345"
                  eventInfo:
                    title: "봄맞이 커피 할인 이벤트"
                    giftName: "아메리카노 1+1"
                    brandColor: "#FF5733"
                    logoUrl: "https://cdn.example.com/logo.png"
              withoutLogo:
                summary: 로고 없는 이벤트
                value:
                  eventDraftId: "evt-draft-67890"
                  eventInfo:
                    title: "신메뉴 출시 기념 경품 추첨"
                    giftName: "스타벅스 기프티콘 5000원권"
                    brandColor: "#00704A"

      responses:
        "202":
          description: |
            이미지 생성 요청이 성공적으로 접수되었습니다.
            jobId를 사용하여 생성 상태를 폴링 조회하세요.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImageGenerationAcceptedResponse"
              examples:
                accepted:
                  summary: 요청 접수 성공
                  value:
                    jobId: "job-img-abc123"
                    eventDraftId: "evt-draft-12345"
                    status: "PENDING"
                    estimatedCompletionTime: 5
                    message: "이미지 생성 요청이 접수되었습니다. jobId로 결과를 조회하세요."

        "400":
          description: 잘못된 요청 (필수 필드 누락, 유효하지 않은 데이터)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                missingField:
                  summary: 필수 필드 누락
                  value:
                    code: "BAD_REQUEST"
                    message: "eventInfo.title은 필수 항목입니다."
                    timestamp: "2025-10-22T14:30:00Z"
                invalidColor:
                  summary: 유효하지 않은 색상 코드
                  value:
                    code: "BAD_REQUEST"
                    message: "brandColor는 HEX 색상 코드 형식이어야 합니다."
                    timestamp: "2025-10-22T14:30:00Z"

        "429":
          description: 요청 제한 초과 (Rate Limiting)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                rateLimitExceeded:
                  summary: 요청 제한 초과
                  value:
                    code: "RATE_LIMIT_EXCEEDED"
                    message: "요청 한도를 초과했습니다. 1분 후 다시 시도하세요."
                    timestamp: "2025-10-22T14:30:00Z"
                    retryAfter: 60

        "500":
          description: 서버 내부 오류
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                internalError:
                  summary: 서버 내부 오류
                  value:
                    code: "INTERNAL_SERVER_ERROR"
                    message: "이미지 생성 요청 처리 중 오류가 발생했습니다."
                    timestamp: "2025-10-22T14:30:00Z"

      security:
        - BearerAuth: []

  /api/v1/content/images/jobs/{jobId}:
    get:
      tags:
        - Job Status
      summary: 이미지 생성 작업 상태 조회 (폴링)
      description: |
        jobId로 이미지 생성 상태를 조회합니다.

        ## 폴링 권장사항
        - **폴링 간격**: 2초
        - **최대 폴링 시간**: 30초
        - **Timeout 후 처리**: 에러 메시지 표시 및 재시도 옵션 제공

        ## 상태 종류
        - **PENDING**: 대기 중 (Kafka Queue에서 대기)
        - **PROCESSING**: 생성 중 (AI API 호출 진행)
        - **COMPLETED**: 완료 (3가지 이미지 URL 반환)
        - **FAILED**: 실패 (에러 메시지 포함, Fallback 이미지 제공)

        ## 캐싱
        - COMPLETED 상태는 Redis 캐싱 (TTL 7일)
        - 동일한 eventDraftId 재요청 시 즉시 반환

      operationId: getImageGenerationStatus
      x-user-story: UFR-CONT-010
      x-controller: ImageGenerationController.getJobStatus
      parameters:
        - name: jobId
          in: path
          required: true
          description: 이미지 생성 Job ID
          schema:
            type: string
            example: "job-img-abc123"

      responses:
        "200":
          description: 이미지 생성 상태 조회 성공
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImageGenerationStatusResponse"
              examples:
                pending:
                  summary: 대기 중
                  value:
                    jobId: "job-img-abc123"
                    status: "PENDING"
                    message: "이미지 생성 대기 중입니다."
                    estimatedCompletionTime: 5

                processing:
                  summary: 생성 중
                  value:
                    jobId: "job-img-abc123"
                    status: "PROCESSING"
                    message: "AI가 이벤트에 어울리는 이미지를 생성하고 있어요..."
                    estimatedCompletionTime: 3

                completed:
                  summary: 생성 완료
                  value:
                    jobId: "job-img-abc123"
                    status: "COMPLETED"
                    message: "이미지 생성이 완료되었습니다."
                    images:
                      - style: "SIMPLE"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                      - style: "FANCY"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-fancy.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                      - style: "TRENDY"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-trendy.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                    completedAt: "2025-10-22T14:30:05Z"
                    fromCache: false

                completedFromCache:
                  summary: 캐시에서 반환
                  value:
                    jobId: "job-img-def456"
                    status: "COMPLETED"
                    message: "이미지 생성이 완료되었습니다. (캐시)"
                    images:
                      - style: "SIMPLE"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                      - style: "FANCY"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-fancy.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                      - style: "TRENDY"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-trendy.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                    completedAt: "2025-10-22T14:28:00Z"
                    fromCache: true

                failed:
                  summary: 생성 실패
                  value:
                    jobId: "job-img-abc123"
                    status: "FAILED"
                    message: "이미지 생성에 실패했습니다."
                    error:
                      code: "IMAGE_GENERATION_FAILED"
                      detail: "외부 AI API 응답 시간 초과. 기본 템플릿으로 대체되었습니다."
                    completedAt: "2025-10-22T14:30:25Z"

        "404":
          description: Job ID를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                notFound:
                  summary: Job ID 없음
                  value:
                    code: "NOT_FOUND"
                    message: "Job ID를 찾을 수 없습니다."
                    timestamp: "2025-10-22T14:30:00Z"

        "500":
          description: 서버 내부 오류
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                internalError:
                  summary: 서버 내부 오류
                  value:
                    code: "INTERNAL_SERVER_ERROR"
                    message: "상태 조회 중 오류가 발생했습니다."
                    timestamp: "2025-10-22T14:30:00Z"

      security:
        - BearerAuth: []

  /api/v1/content/events/{eventDraftId}:
    get:
      tags:
        - Content Management
      summary: 이벤트의 생성된 콘텐츠 조회
      description: |
        특정 이벤트의 생성된 모든 콘텐츠(이미지) 조회
        - Redis 캐시에서 조회
        - TTL 7일 이내 데이터만 조회 가능
        - 캐시 만료 시 404 반환

      operationId: getContentByEventId
      x-user-story: UFR-CONT-020
      x-controller: ContentController.getContentByEventId
      parameters:
        - name: eventDraftId
          in: path
          required: true
          description: 이벤트 초안 ID
          schema:
            type: string
            example: "evt-draft-12345"

      responses:
        "200":
          description: 콘텐츠 조회 성공
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContentResponse"
              examples:
                success:
                  summary: 콘텐츠 조회 성공
                  value:
                    eventDraftId: "evt-draft-12345"
                    images:
                      - imageId: "img-12345-simple"
                        style: "SIMPLE"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                        createdAt: "2025-10-22T14:30:05Z"
                      - imageId: "img-12345-fancy"
                        style: "FANCY"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-fancy.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                        createdAt: "2025-10-22T14:30:10Z"
                      - imageId: "img-12345-trendy"
                        style: "TRENDY"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-trendy.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                        createdAt: "2025-10-22T14:30:15Z"
                    totalCount: 3
                    createdAt: "2025-10-22T14:30:00Z"
                    expiresAt: "2025-10-29T14:30:00Z"

        "404":
          description: 콘텐츠를 찾을 수 없음 (생성 중이거나 만료됨)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                notFound:
                  summary: 콘텐츠 없음
                  value:
                    code: "CONTENT_NOT_FOUND"
                    message: "해당 이벤트의 콘텐츠를 찾을 수 없습니다."
                    timestamp: "2025-10-22T14:30:00Z"

        "500":
          description: 서버 내부 오류
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

      security:
        - BearerAuth: []

  /api/v1/content/events/{eventDraftId}/images:
    get:
      tags:
        - Content Management
      summary: 이벤트의 이미지 목록 조회 (필터링)
      description: |
        특정 이벤트의 모든 생성 이미지 목록 조회
        - 스타일별, 플랫폼별 필터링 지원

      operationId: getImages
      x-user-story: UFR-CONT-020
      x-controller: ContentController.getImages
      parameters:
        - name: eventDraftId
          in: path
          required: true
          description: 이벤트 초안 ID
          schema:
            type: string
            example: "evt-draft-12345"
        - name: style
          in: query
          required: false
          description: 이미지 스타일 필터
          schema:
            type: string
            enum: [SIMPLE, FANCY, TRENDY]
            example: "SIMPLE"
        - name: platform
          in: query
          required: false
          description: 플랫폼 필터
          schema:
            type: string
            enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL]
            example: "INSTAGRAM"

      responses:
        "200":
          description: 이미지 목록 조회 성공
          content:
            application/json:
              schema:
                type: object
                properties:
                  eventDraftId:
                    type: string
                  totalCount:
                    type: integer
                  images:
                    type: array
                    items:
                      $ref: "#/components/schemas/GeneratedImage"
              examples:
                allImages:
                  summary: 전체 이미지 조회
                  value:
                    eventDraftId: "evt-draft-12345"
                    totalCount: 3
                    images:
                      - imageId: "img-12345-simple"
                        style: "SIMPLE"
                        url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"
                        platform: "INSTAGRAM"
                        size:
                          width: 1080
                          height: 1080
                        createdAt: "2025-10-22T14:30:05Z"

        "404":
          description: 이미지를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

      security:
        - BearerAuth: []

  /api/v1/content/images/{imageId}:
    get:
      tags:
        - Image Management
      summary: 특정 이미지 상세 조회
      description: 이미지 ID로 특정 이미지의 상세 정보 조회

      operationId: getImageById
      x-user-story: UFR-CONT-020
      x-controller: ContentController.getImageById
      parameters:
        - name: imageId
          in: path
          required: true
          description: 이미지 ID
          schema:
            type: string
            example: "img-12345-simple"

      responses:
        "200":
          description: 이미지 조회 성공
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GeneratedImage"
              examples:
                success:
                  summary: 이미지 조회 성공
                  value:
                    imageId: "img-12345-simple"
                    style: "SIMPLE"
                    url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"
                    platform: "INSTAGRAM"
                    size:
                      width: 1080
                      height: 1080
                    createdAt: "2025-10-22T14:30:05Z"

        "404":
          description: 이미지를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

      security:
        - BearerAuth: []

    delete:
      tags:
        - Image Management
      summary: 생성된 이미지 삭제
      description: |
        특정 이미지 삭제
        - Redis 캐시에서 제거
        - CDN 이미지는 유지 (비용 고려)

      operationId: deleteImage
      x-user-story: UFR-CONT-020
      x-controller: ContentController.deleteImage
      parameters:
        - name: imageId
          in: path
          required: true
          description: 이미지 ID
          schema:
            type: string
            example: "img-12345-simple"

      responses:
        "204":
          description: 이미지 삭제 성공

        "404":
          description: 이미지를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

      security:
        - BearerAuth: []

  /api/v1/content/images/{imageId}/regenerate:
    post:
      tags:
        - Image Management
      summary: 이미지 재생성 요청
      description: |
        특정 이미지를 재생성 (콘텐츠 편집)
        - 동일한 스타일/플랫폼으로 재생성
        - 프롬프트 수정 가능
        - 비동기 처리 (Kafka Job 발행)

      operationId: regenerateImage
      x-user-story: UFR-CONT-020
      x-controller: ContentController.regenerateImage
      parameters:
        - name: imageId
          in: path
          required: true
          description: 이미지 ID
          schema:
            type: string
            example: "img-12345-simple"

      requestBody:
        required: false
        description: 재생성 옵션 (선택사항)
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImageRegenerationRequest"
            examples:
              modifyPrompt:
                summary: 프롬프트 수정
                value:
                  content: "밝은 분위기로 변경"
              changeStyle:
                summary: 스타일 변경
                value:
                  style: "FANCY"

      responses:
        "202":
          description: 재생성 요청 접수 (비동기 처리)
          content:
            application/json:
              schema:
                type: object
                required:
                  - message
                  - jobId
                  - estimatedTime
                properties:
                  message:
                    type: string
                    example: "이미지 재생성 요청이 접수되었습니다"
                  jobId:
                    type: string
                    example: "job-regen-abc123"
                  estimatedTime:
                    type: integer
                    description: 예상 소요 시간 (초)
                    example: 10

        "404":
          description: 원본 이미지를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

        "503":
          description: 이미지 생성 서비스 장애 (Circuit Breaker Open)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CircuitBreakerErrorResponse"

      security:
        - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT 토큰을 Authorization 헤더에 포함 (Bearer {token})

  schemas:
    # ========================================
    # Kafka Job Schema (비동기 처리)
    # ========================================
    ImageGenerationJob:
      type: object
      description: |
        **Kafka Topic**: `image-generation-job`

        Event Service에서 발행하여 Content Service가 소비하는 Job Payload
        - Content Service의 Kafka Consumer가 처리
        - 비동기 이미지 생성 작업 수행
      required:
        - jobId
        - eventDraftId
        - eventInfo
      properties:
        jobId:
          type: string
          description: Job ID (작업 추적용)
          example: "job-img-abc123"

        eventDraftId:
          type: string
          description: 이벤트 초안 ID
          example: "evt-draft-12345"

        eventInfo:
          type: object
          description: 이벤트 정보 (AI Service에서 생성)
          required:
            - title
            - giftName
          properties:
            title:
              type: string
              description: 이벤트 제목
              example: "봄맞이 커피 할인 이벤트"
            giftName:
              type: string
              description: 경품명
              example: "아메리카노 1+1"
            brandColor:
              type: string
              description: 브랜드 컬러 (HEX)
              pattern: "^#[0-9A-Fa-f]{6}$"
              example: "#FF5733"
            logoUrl:
              type: string
              format: uri
              description: 로고 이미지 URL (선택)
              example: "https://cdn.example.com/logo.png"

        styles:
          type: array
          description: 생성할 스타일 목록 (기본값 전체)
          items:
            type: string
            enum: [SIMPLE, FANCY, TRENDY]
          example: ["SIMPLE", "FANCY", "TRENDY"]

        platforms:
          type: array
          description: 생성할 플랫폼 목록 (기본값 Instagram)
          items:
            type: string
            enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL]
          example: ["INSTAGRAM"]

        priority:
          type: integer
          description: 우선순위 (1-10, 높을수록 우선)
          minimum: 1
          maximum: 10
          example: 5

        requestedAt:
          type: string
          format: date-time
          description: 요청 시각
          example: "2025-10-22T14:00:00Z"

    # ========================================
    # Request Schemas
    # ========================================
    ImageGenerationRequest:
      type: object
      required:
        - eventDraftId
        - eventInfo
      properties:
        eventDraftId:
          type: string
          description: |
            이벤트 초안 ID (Event Service에서 발급)
            동일한 eventDraftId로 재요청 시 캐시된 이미지 반환
          example: "evt-draft-12345"

        eventInfo:
          type: object
          required:
            - title
            - giftName
          properties:
            title:
              type: string
              description: 이벤트 제목 (최대 50자)
              minLength: 1
              maxLength: 50
              example: "봄맞이 커피 할인 이벤트"

            giftName:
              type: string
              description: 경품명 (최대 30자)
              minLength: 1
              maxLength: 30
              example: "아메리카노 1+1"

            brandColor:
              type: string
              description: |
                브랜드 컬러 (HEX 색상 코드)
                사용자 프로필에서 가져오거나 기본값 사용
              pattern: "^#[0-9A-Fa-f]{6}$"
              example: "#FF5733"

            logoUrl:
              type: string
              format: uri
              description: |
                로고 이미지 URL (선택)
                업로드된 경우에만 제공
              example: "https://cdn.example.com/logo.png"

    ImageGenerationAcceptedResponse:
      type: object
      required:
        - jobId
        - eventDraftId
        - status
        - message
      properties:
        jobId:
          type: string
          description: 이미지 생성 Job ID (폴링 조회에 사용)
          example: "job-img-abc123"

        eventDraftId:
          type: string
          description: 이벤트 초안 ID
          example: "evt-draft-12345"

        status:
          type: string
          enum: [PENDING]
          description: 초기 상태는 항상 PENDING
          example: "PENDING"

        estimatedCompletionTime:
          type: integer
          description: 예상 완료 시간 (초)
          example: 5

        message:
          type: string
          description: 응답 메시지
          example: "이미지 생성 요청이 접수되었습니다. jobId로 결과를 조회하세요."

    ImageGenerationStatusResponse:
      type: object
      required:
        - jobId
        - status
        - message
      properties:
        jobId:
          type: string
          description: 이미지 생성 Job ID
          example: "job-img-abc123"

        status:
          type: string
          enum: [PENDING, PROCESSING, COMPLETED, FAILED]
          description: |
            Job 상태
            - PENDING: 대기 중
            - PROCESSING: 생성 중
            - COMPLETED: 완료
            - FAILED: 실패
          example: "COMPLETED"

        message:
          type: string
          description: 상태 메시지
          example: "이미지 생성이 완료되었습니다."

        estimatedCompletionTime:
          type: integer
          description: 예상 완료 시간 (초, PENDING/PROCESSING 상태에서만)
          example: 3

        images:
          type: array
          description: 생성된 이미지 배열 (COMPLETED 상태에서만)
          items:
            $ref: "#/components/schemas/GeneratedImage"

        completedAt:
          type: string
          format: date-time
          description: 완료 시각 (COMPLETED/FAILED 상태에서만)
          example: "2025-10-22T14:30:05Z"

        fromCache:
          type: boolean
          description: 캐시에서 반환 여부 (COMPLETED 상태에서만)
          example: false

        error:
          $ref: "#/components/schemas/JobError"

    GeneratedImage:
      type: object
      required:
        - style
        - url
        - platform
        - size
      properties:
        style:
          type: string
          enum: [SIMPLE, FANCY, TRENDY]
          description: |
            이미지 스타일
            - SIMPLE: 심플 스타일 (깔끔한 디자인, 텍스트 중심)
            - FANCY: 화려한 스타일 (눈에 띄는 디자인, 풍부한 색상)
            - TRENDY: 트렌디 스타일 (최신 트렌드, MZ세대 타겟)
          example: "SIMPLE"

        url:
          type: string
          format: uri
          description: CDN 이미지 URL
          example: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"

        platform:
          type: string
          enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL]
          description: |
            플랫폼별 최적화
            - INSTAGRAM: 1080x1080
            - NAVER_BLOG: 800x600
            - KAKAO_CHANNEL: 800x800
          example: "INSTAGRAM"

        size:
          type: object
          required:
            - width
            - height
          properties:
            width:
              type: integer
              description: 이미지 너비 (픽셀)
              example: 1080

            height:
              type: integer
              description: 이미지 높이 (픽셀)
              example: 1080

    JobError:
      type: object
      required:
        - code
        - detail
      description: Job 실패 시 에러 정보 (FAILED 상태에서만)
      properties:
        code:
          type: string
          description: 에러 코드
          example: "IMAGE_GENERATION_FAILED"

        detail:
          type: string
          description: 상세 에러 메시지
          example: "외부 AI API 응답 시간 초과. 기본 템플릿으로 대체되었습니다."

    ErrorResponse:
      type: object
      required:
        - code
        - message
        - timestamp
      properties:
        code:
          type: string
          description: 에러 코드
          example: "BAD_REQUEST"

        message:
          type: string
          description: 에러 메시지
          example: "eventInfo.title은 필수 항목입니다."

        timestamp:
          type: string
          format: date-time
          description: 에러 발생 시각
          example: "2025-10-22T14:30:00Z"

        retryAfter:
          type: integer
          description: 재시도 대기 시간 (초, Rate Limiting 에러에서만)
          example: 60

    # ========================================
    # Content Management Schemas (UFR-CONT-020)
    # ========================================
    ContentResponse:
      type: object
      description: 이벤트의 생성된 콘텐츠 전체 정보
      required:
        - eventDraftId
        - images
        - totalCount
        - createdAt
        - expiresAt
      properties:
        eventDraftId:
          type: string
          description: 이벤트 초안 ID
          example: "evt-draft-12345"

        images:
          type: array
          description: 생성된 이미지 목록
          items:
            $ref: "#/components/schemas/ImageDetail"

        totalCount:
          type: integer
          description: 총 이미지 개수
          example: 3

        createdAt:
          type: string
          format: date-time
          description: 콘텐츠 생성 시각
          example: "2025-10-22T14:30:00Z"

        expiresAt:
          type: string
          format: date-time
          description: 캐시 만료 시각 (TTL 7일)
          example: "2025-10-29T14:30:00Z"

    ImageDetail:
      type: object
      description: 상세 이미지 정보 (생성 시각 포함)
      required:
        - imageId
        - style
        - url
        - platform
        - size
        - createdAt
      properties:
        imageId:
          type: string
          description: 이미지 ID
          example: "img-12345-simple"

        style:
          type: string
          enum: [SIMPLE, FANCY, TRENDY]
          description: 이미지 스타일
          example: "SIMPLE"

        url:
          type: string
          format: uri
          description: CDN 이미지 URL (Azure Blob Storage)
          example: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png"

        platform:
          type: string
          enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL]
          description: 플랫폼
          example: "INSTAGRAM"

        size:
          type: object
          required:
            - width
            - height
          properties:
            width:
              type: integer
              description: 이미지 너비 (픽셀)
              example: 1080
            height:
              type: integer
              description: 이미지 높이 (픽셀)
              example: 1080

        createdAt:
          type: string
          format: date-time
          description: 이미지 생성 시각
          example: "2025-10-22T14:30:05Z"

        fallbackUsed:
          type: boolean
          description: Fallback 이미지 사용 여부
          example: false

    ImageRegenerationRequest:
      type: object
      description: 이미지 재생성 요청 (콘텐츠 편집)
      properties:
        content:
          type: string
          description: 수정된 프롬프트 (선택사항)
          example: "밝은 분위기로 변경"

        style:
          type: string
          description: 변경할 스타일 (선택사항)
          enum: [SIMPLE, FANCY, TRENDY]
          example: "FANCY"

    CircuitBreakerErrorResponse:
      type: object
      description: Circuit Breaker 오류 응답 (외부 API 장애)
      required:
        - code
        - message
        - timestamp
        - circuitBreakerState
      properties:
        code:
          type: string
          description: 에러 코드
          example: "IMAGE_GENERATION_SERVICE_UNAVAILABLE"

        message:
          type: string
          description: 에러 메시지
          example: "이미지 생성 서비스가 일시적으로 사용 불가능합니다"

        timestamp:
          type: string
          format: date-time
          description: 에러 발생 시각
          example: "2025-10-22T14:30:00Z"

        circuitBreakerState:
          type: string
          enum: [OPEN, HALF_OPEN, CLOSED]
          description: Circuit Breaker 상태
          example: "OPEN"

        fallbackAvailable:
          type: boolean
          description: Fallback 이미지 사용 가능 여부
          example: true

        fallbackImageUrl:
          type: string
          format: uri
          description: Fallback 템플릿 이미지 URL (사용 가능한 경우)
          example: "https://cdn.kt-event.com/templates/default_event.png"

        retryAfter:
          type: integer
          description: 재시도 가능 시간 (초)
          example: 300
