openapi: 3.0.3
info:
  title: Notification Service API
  description: |
    회의록 작성 및 공유 개선 서비스의 알림 발송 및 리마인더 관리 API

    ## 주요 기능
    - 이메일 알림 발송 (회의 초대, Todo 할당, 리마인더)
    - 알림 이력 조회 및 관리
    - 사용자 알림 설정 관리

    ## Event-Driven Architecture
    - 알림 발송은 Azure Event Hubs를 통한 이벤트 기반 처리
    - 비동기 처리로 성능 최적화
    - 재시도 메커니즘으로 신뢰성 보장
  version: 1.0.0
  contact:
    name: Backend Development Team
    email: backend@example.com

servers:
  - url: https://api.hgzero.com/notification/v1
    description: Production Server
  - url: https://dev-api.hgzero.com/notification/v1
    description: Development Server
  - url: http://localhost:8083/api/v1
    description: Local Development

tags:
  - name: Notification
    description: 알림 발송 및 관리 API
  - name: Notification Settings
    description: 사용자 알림 설정 API
  - name: Internal
    description: 내부 서비스 간 통신 API (Event Handler)

paths:
  # ========================================
  # Notification APIs
  # ========================================

  /notifications/invitation:
    post:
      summary: 회의 초대 알림 발송 (내부 API)
      description: |
        MeetingCreated 이벤트를 수신하여 참석자에게 회의 초대 이메일을 발송합니다.
        - Event-Driven 방식으로 Meeting Service에서 이벤트 발행
        - 참석자별 병렬 이메일 발송
        - 발송 이력 저장 및 재시도 메커니즘
      operationId: sendMeetingInvitation
      x-user-story: UFR-MEET-010
      x-controller: NotificationController
      tags:
        - Internal
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MeetingInvitationRequest'
            example:
              meetingId: "MTG-20250123-001"
              title: "2025년 1분기 전략 회의"
              scheduledAt: "2025-01-30T14:00:00Z"
              location: "회의실 A"
              participants:
                - userId: "U001"
                  userName: "김철수"
                  email: "kim@example.com"
                - userId: "U002"
                  userName: "이영희"
                  email: "lee@example.com"
              creatorName: "박팀장"
              meetingLink: "https://meet.hgzero.com/MTG-20250123-001"
              calendarLink: "https://calendar.hgzero.com/add/MTG-20250123-001"
      responses:
        '200':
          description: 알림 발송 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationResponse'
              example:
                notificationId: "NTF-20250123-001"
                status: "SENT"
                sentCount: 2
                failedCount: 0
                createdAt: "2025-01-23T10:00:00Z"
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /notifications/todo:
    post:
      summary: Todo 할당 알림 발송 (내부 API)
      description: |
        TodoAssigned 이벤트를 수신하여 담당자에게 Todo 알림 이메일을 발송합니다.
        - 할당 즉시 알림
        - 마감일 3일 전, 1일 전, 당일 리마인더 스케줄링
        - 회의록 링크 포함
      operationId: sendTodoNotification
      x-user-story: UFR-TODO-010
      x-controller: NotificationController
      tags:
        - Internal
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TodoNotificationRequest'
            example:
              todoId: "TODO-20250123-001"
              meetingId: "MTG-20250123-001"
              assignee:
                userId: "U001"
                userName: "김철수"
                email: "kim@example.com"
              content: "API 설계서 작성 및 검토"
              dueDate: "2025-01-30T23:59:59Z"
              priority: "HIGH"
              minutesLink: "https://hgzero.com/minutes/MTG-20250123-001#section-3"
              meetingTitle: "2025년 1분기 전략 회의"
      responses:
        '200':
          description: 알림 발송 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationResponse'
              example:
                notificationId: "NTF-20250123-002"
                status: "SENT"
                sentCount: 1
                failedCount: 0
                createdAt: "2025-01-23T10:05:00Z"
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /notifications:
    get:
      summary: 사용자별 알림 이력 조회
      description: |
        사용자가 받은 알림 이력을 조회합니다.
        - 알림 유형별 필터링 (초대, Todo, 리마인더)
        - 상태별 필터링 (발송완료, 발송실패)
        - 페이징 지원
      operationId: getNotifications
      x-user-story: AFR-USER-020
      x-controller: NotificationController
      tags:
        - Notification
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
        - name: type
          in: query
          description: 알림 유형 필터
          required: false
          schema:
            type: string
            enum:
              - INVITATION
              - TODO_ASSIGNED
              - REMINDER
              - TODO_REMINDER
        - name: status
          in: query
          description: 알림 상태 필터
          required: false
          schema:
            type: string
            enum:
              - SENT
              - FAILED
              - PENDING
        - name: page
          in: query
          description: 페이지 번호 (0부터 시작)
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: size
          in: query
          description: 페이지당 항목 수
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        '200':
          description: 알림 이력 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationListResponse'
              example:
                notifications:
                  - notificationId: "NTF-20250123-002"
                    type: "TODO_ASSIGNED"
                    title: "[TODO 할당] API 설계서 작성 및 검토"
                    content: "Todo가 할당되었습니다. 마감일: 2025-01-30"
                    status: "SENT"
                    sentAt: "2025-01-23T10:05:00Z"
                    relatedId: "TODO-20250123-001"
                  - notificationId: "NTF-20250123-001"
                    type: "INVITATION"
                    title: "[회의 초대] 2025년 1분기 전략 회의"
                    content: "회의에 초대되었습니다. 일시: 2025-01-30 14:00"
                    status: "SENT"
                    sentAt: "2025-01-23T10:00:00Z"
                    relatedId: "MTG-20250123-001"
                pagination:
                  currentPage: 0
                  pageSize: 20
                  totalElements: 2
                  totalPages: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /notifications/{notificationId}:
    get:
      summary: 특정 알림 상세 조회
      description: |
        알림 ID로 특정 알림의 상세 정보를 조회합니다.
        - 발송 상태
        - 수신자 정보
        - 템플릿 정보
        - 재시도 이력
      operationId: getNotificationById
      x-user-story: AFR-USER-020
      x-controller: NotificationController
      tags:
        - Notification
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
        - name: notificationId
          in: path
          description: 알림 ID
          required: true
          schema:
            type: string
            pattern: '^NTF-[0-9]{8}-[0-9]{3}$'
            example: "NTF-20250123-001"
      responses:
        '200':
          description: 알림 상세 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationDetailResponse'
              example:
                notificationId: "NTF-20250123-001"
                type: "INVITATION"
                title: "[회의 초대] 2025년 1분기 전략 회의"
                content: "회의에 초대되었습니다."
                status: "SENT"
                relatedId: "MTG-20250123-001"
                recipients:
                  - email: "kim@example.com"
                    status: "SENT"
                    sentAt: "2025-01-23T10:00:05Z"
                  - email: "lee@example.com"
                    status: "SENT"
                    sentAt: "2025-01-23T10:00:06Z"
                templateId: "meeting-invitation"
                createdAt: "2025-01-23T10:00:00Z"
                completedAt: "2025-01-23T10:00:10Z"
                retryCount: 0
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ========================================
  # Notification Settings APIs
  # ========================================

  /notifications/settings:
    get:
      summary: 사용자 알림 설정 조회
      description: |
        사용자의 알림 설정을 조회합니다.
        - 알림 채널 설정 (이메일/SMS)
        - 알림 유형별 활성화 여부
        - 수신 시간대 설정
      operationId: getNotificationSettings
      x-user-story: AFR-USER-020
      x-controller: NotificationSettingsController
      tags:
        - Notification Settings
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
      responses:
        '200':
          description: 알림 설정 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationSettingsResponse'
              example:
                userId: "U001"
                channels:
                  email: true
                  sms: false
                preferences:
                  invitationEnabled: true
                  todoEnabled: true
                  reminderEnabled: true
                  minutesUpdateEnabled: false
                quietHours:
                  enabled: true
                  startTime: "22:00"
                  endTime: "08:00"
                  timezone: "Asia/Seoul"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      summary: 사용자 알림 설정 업데이트
      description: |
        사용자의 알림 설정을 변경합니다.
        - 부분 업데이트 지원
        - 설정 변경 즉시 적용
      operationId: updateNotificationSettings
      x-user-story: AFR-USER-020
      x-controller: NotificationSettingsController
      tags:
        - Notification Settings
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NotificationSettingsUpdateRequest'
            example:
              channels:
                email: true
                sms: false
              preferences:
                invitationEnabled: true
                todoEnabled: true
                reminderEnabled: false
                minutesUpdateEnabled: false
              quietHours:
                enabled: true
                startTime: "23:00"
                endTime: "07:00"
                timezone: "Asia/Seoul"
      responses:
        '200':
          description: 알림 설정 업데이트 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

# ========================================
# Components
# ========================================

components:
  schemas:
    # Request Schemas
    MeetingInvitationRequest:
      type: object
      required:
        - meetingId
        - title
        - scheduledAt
        - participants
        - creatorName
      properties:
        meetingId:
          type: string
          description: 회의 ID
          pattern: '^MTG-[0-9]{8}-[0-9]{3}$'
          example: "MTG-20250123-001"
        title:
          type: string
          description: 회의 제목
          maxLength: 100
          example: "2025년 1분기 전략 회의"
        scheduledAt:
          type: string
          format: date-time
          description: 회의 시작 일시 (ISO 8601)
          example: "2025-01-30T14:00:00Z"
        location:
          type: string
          description: 회의 장소
          maxLength: 200
          example: "회의실 A"
        participants:
          type: array
          description: 참석자 목록
          minItems: 1
          items:
            $ref: '#/components/schemas/Participant'
        creatorName:
          type: string
          description: 회의 생성자 이름
          example: "박팀장"
        meetingLink:
          type: string
          format: uri
          description: 회의 참여 링크
          example: "https://meet.hgzero.com/MTG-20250123-001"
        calendarLink:
          type: string
          format: uri
          description: 캘린더 추가 링크
          example: "https://calendar.hgzero.com/add/MTG-20250123-001"

    TodoNotificationRequest:
      type: object
      required:
        - todoId
        - meetingId
        - assignee
        - content
        - dueDate
        - priority
        - minutesLink
        - meetingTitle
      properties:
        todoId:
          type: string
          description: Todo ID
          pattern: '^TODO-[0-9]{8}-[0-9]{3}$'
          example: "TODO-20250123-001"
        meetingId:
          type: string
          description: 관련 회의 ID
          pattern: '^MTG-[0-9]{8}-[0-9]{3}$'
          example: "MTG-20250123-001"
        assignee:
          $ref: '#/components/schemas/Participant'
        content:
          type: string
          description: Todo 내용
          maxLength: 500
          example: "API 설계서 작성 및 검토"
        dueDate:
          type: string
          format: date-time
          description: 마감일 (ISO 8601)
          example: "2025-01-30T23:59:59Z"
        priority:
          type: string
          description: 우선순위
          enum:
            - HIGH
            - MEDIUM
            - LOW
          example: "HIGH"
        minutesLink:
          type: string
          format: uri
          description: 회의록 링크 (해당 섹션)
          example: "https://hgzero.com/minutes/MTG-20250123-001#section-3"
        meetingTitle:
          type: string
          description: 회의 제목
          example: "2025년 1분기 전략 회의"

    NotificationSettingsUpdateRequest:
      type: object
      properties:
        channels:
          type: object
          description: 알림 채널 설정
          properties:
            email:
              type: boolean
              description: 이메일 알림 활성화
            sms:
              type: boolean
              description: SMS 알림 활성화
        preferences:
          type: object
          description: 알림 유형별 활성화 설정
          properties:
            invitationEnabled:
              type: boolean
              description: 회의 초대 알림
            todoEnabled:
              type: boolean
              description: Todo 할당 알림
            reminderEnabled:
              type: boolean
              description: 리마인더 알림
            minutesUpdateEnabled:
              type: boolean
              description: 회의록 수정 알림
        quietHours:
          type: object
          description: 방해 금지 시간대 설정
          properties:
            enabled:
              type: boolean
              description: 방해 금지 시간대 활성화
            startTime:
              type: string
              format: time
              description: 시작 시간 (HH:mm)
              example: "22:00"
            endTime:
              type: string
              format: time
              description: 종료 시간 (HH:mm)
              example: "08:00"
            timezone:
              type: string
              description: 타임존
              example: "Asia/Seoul"

    # Response Schemas
    NotificationResponse:
      type: object
      required:
        - notificationId
        - status
        - sentCount
        - failedCount
        - createdAt
      properties:
        notificationId:
          type: string
          description: 알림 ID
          pattern: '^NTF-[0-9]{8}-[0-9]{3}$'
          example: "NTF-20250123-001"
        status:
          type: string
          description: 알림 전체 상태
          enum:
            - SENT
            - PARTIAL
            - FAILED
            - PENDING
          example: "SENT"
        sentCount:
          type: integer
          description: 발송 성공 건수
          minimum: 0
          example: 2
        failedCount:
          type: integer
          description: 발송 실패 건수
          minimum: 0
          example: 0
        createdAt:
          type: string
          format: date-time
          description: 알림 생성 일시
          example: "2025-01-23T10:00:00Z"

    NotificationListResponse:
      type: object
      required:
        - notifications
        - pagination
      properties:
        notifications:
          type: array
          items:
            $ref: '#/components/schemas/NotificationSummary'
        pagination:
          $ref: '#/components/schemas/PaginationInfo'

    NotificationSummary:
      type: object
      required:
        - notificationId
        - type
        - title
        - status
        - sentAt
      properties:
        notificationId:
          type: string
          description: 알림 ID
          example: "NTF-20250123-001"
        type:
          type: string
          description: 알림 유형
          enum:
            - INVITATION
            - TODO_ASSIGNED
            - REMINDER
            - TODO_REMINDER
          example: "INVITATION"
        title:
          type: string
          description: 알림 제목
          example: "[회의 초대] 2025년 1분기 전략 회의"
        content:
          type: string
          description: 알림 내용 요약
          example: "회의에 초대되었습니다. 일시: 2025-01-30 14:00"
        status:
          type: string
          description: 발송 상태
          enum:
            - SENT
            - FAILED
            - PENDING
          example: "SENT"
        sentAt:
          type: string
          format: date-time
          description: 발송 일시
          example: "2025-01-23T10:00:00Z"
        relatedId:
          type: string
          description: 관련 객체 ID (회의 ID, Todo ID 등)
          example: "MTG-20250123-001"

    NotificationDetailResponse:
      type: object
      required:
        - notificationId
        - type
        - title
        - content
        - status
        - relatedId
        - recipients
        - createdAt
      properties:
        notificationId:
          type: string
          description: 알림 ID
          example: "NTF-20250123-001"
        type:
          type: string
          description: 알림 유형
          enum:
            - INVITATION
            - TODO_ASSIGNED
            - REMINDER
            - TODO_REMINDER
        title:
          type: string
          description: 알림 제목
          example: "[회의 초대] 2025년 1분기 전략 회의"
        content:
          type: string
          description: 알림 전체 내용
          example: "회의에 초대되었습니다."
        status:
          type: string
          description: 알림 전체 상태
          enum:
            - SENT
            - PARTIAL
            - FAILED
            - PENDING
        relatedId:
          type: string
          description: 관련 객체 ID
          example: "MTG-20250123-001"
        recipients:
          type: array
          description: 수신자별 발송 상태
          items:
            type: object
            properties:
              email:
                type: string
                format: email
                description: 수신자 이메일
              status:
                type: string
                enum:
                  - SENT
                  - FAILED
                  - PENDING
              sentAt:
                type: string
                format: date-time
                description: 발송 일시
              errorMessage:
                type: string
                description: 오류 메시지 (실패 시)
        templateId:
          type: string
          description: 사용된 템플릿 ID
          example: "meeting-invitation"
        createdAt:
          type: string
          format: date-time
          description: 알림 생성 일시
        completedAt:
          type: string
          format: date-time
          description: 알림 완료 일시
        retryCount:
          type: integer
          description: 재시도 횟수
          minimum: 0

    NotificationSettingsResponse:
      type: object
      required:
        - userId
        - channels
        - preferences
      properties:
        userId:
          type: string
          description: 사용자 ID
          example: "U001"
        channels:
          type: object
          description: 알림 채널 설정
          required:
            - email
            - sms
          properties:
            email:
              type: boolean
              description: 이메일 알림 활성화
            sms:
              type: boolean
              description: SMS 알림 활성화
        preferences:
          type: object
          description: 알림 유형별 활성화 설정
          required:
            - invitationEnabled
            - todoEnabled
            - reminderEnabled
            - minutesUpdateEnabled
          properties:
            invitationEnabled:
              type: boolean
              description: 회의 초대 알림
            todoEnabled:
              type: boolean
              description: Todo 할당 알림
            reminderEnabled:
              type: boolean
              description: 리마인더 알림
            minutesUpdateEnabled:
              type: boolean
              description: 회의록 수정 알림
        quietHours:
          type: object
          description: 방해 금지 시간대 설정
          properties:
            enabled:
              type: boolean
            startTime:
              type: string
              format: time
            endTime:
              type: string
              format: time
            timezone:
              type: string

    # Common Schemas
    Participant:
      type: object
      required:
        - userId
        - userName
        - email
      properties:
        userId:
          type: string
          description: 사용자 ID
          example: "U001"
        userName:
          type: string
          description: 사용자 이름
          example: "김철수"
        email:
          type: string
          format: email
          description: 이메일 주소
          example: "kim@example.com"

    PaginationInfo:
      type: object
      required:
        - currentPage
        - pageSize
        - totalElements
        - totalPages
      properties:
        currentPage:
          type: integer
          description: 현재 페이지 번호 (0부터 시작)
          minimum: 0
          example: 0
        pageSize:
          type: integer
          description: 페이지당 항목 수
          minimum: 1
          example: 20
        totalElements:
          type: integer
          description: 전체 항목 수
          minimum: 0
          example: 2
        totalPages:
          type: integer
          description: 전체 페이지 수
          minimum: 0
          example: 1

    ErrorResponse:
      type: object
      required:
        - code
        - message
        - timestamp
      properties:
        code:
          type: string
          description: 오류 코드
          example: "INVALID_REQUEST"
        message:
          type: string
          description: 오류 메시지
          example: "Invalid request parameters"
        timestamp:
          type: string
          format: date-time
          description: 오류 발생 시각
        details:
          type: array
          description: 상세 오류 정보
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string

  parameters:
    UserIdHeader:
      name: X-User-Id
      in: header
      description: 사용자 ID (JWT 토큰에서 추출)
      required: true
      schema:
        type: string
        example: "U001"

  responses:
    BadRequest:
      description: 잘못된 요청
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: "INVALID_REQUEST"
            message: "Invalid request parameters"
            timestamp: "2025-01-23T10:00:00Z"
            details:
              - field: "meetingId"
                message: "Meeting ID is required"

    Unauthorized:
      description: 인증 실패
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: "UNAUTHORIZED"
            message: "Authentication required"
            timestamp: "2025-01-23T10:00:00Z"

    NotFound:
      description: 리소스를 찾을 수 없음
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: "NOT_FOUND"
            message: "Notification not found"
            timestamp: "2025-01-23T10:00:00Z"

    InternalServerError:
      description: 서버 내부 오류
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: "INTERNAL_SERVER_ERROR"
            message: "An unexpected error occurred"
            timestamp: "2025-01-23T10:00:00Z"

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT 토큰 기반 인증

security:
  - BearerAuth: []
