openapi: 3.0.0
info:
  title: Meeting Service API
  description: |
    회의록 작성 및 공유 개선 서비스의 Meeting Service API

    **주요 기능:**
    - 회의 관리 (예약, 시작, 종료)
    - 회의록 관리 (생성, 수정, 확정, 조회)
    - Todo 관리 (할당, 진행, 완료)
    - 실시간 협업 (동기화, 충돌해결, 검증)
    - 템플릿 관리
    - 대시보드
  version: 1.0.0
  contact:
    name: HGZero Team
    email: support@hgzero.com

servers:
  - url: https://api.hgzero.com/meeting/v1
    description: Production server
  - url: https://dev-api.hgzero.com/meeting/v1
    description: Development server
  - url: http://localhost:8080/api
    description: Local development server

tags:
  - name: Dashboard
    description: 대시보드 관련 API
  - name: Meeting
    description: 회의 관리 API
  - name: Minutes
    description: 회의록 관리 API
  - name: Todo
    description: Todo 관리 API
  - name: Collaboration
    description: 실시간 협업 API
  - name: Template
    description: 템플릿 관리 API
  - name: WebSocket
    description: WebSocket 엔드포인트

paths:
  # ==================== Dashboard APIs ====================
  /dashboard:
    get:
      tags:
        - Dashboard
      summary: 대시보드 데이터 조회
      description: |
        사용자별 맞춤 대시보드 정보를 조회합니다.
        - 예정된 회의 목록
        - 진행 중 Todo 목록
        - 최근 회의록 목록
        - 공유받은 회의록 목록
        - 통계 정보
      operationId: getDashboard
      x-user-story: AFR-USER-020
      x-controller: DashboardController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
        - $ref: '#/components/parameters/UserNameHeader'
        - $ref: '#/components/parameters/UserEmailHeader'
      responses:
        '200':
          description: 대시보드 데이터 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DashboardResponse'
              example:
                upcomingMeetings:
                  - meetingId: "550e8400-e29b-41d4-a716-446655440000"
                    title: "Q1 전략 회의"
                    startTime: "2025-01-25T14:00:00Z"
                    endTime: "2025-01-25T16:00:00Z"
                    location: "회의실 A"
                    participantCount: 5
                    status: "SCHEDULED"
                  - meetingId: "550e8400-e29b-41d4-a716-446655440001"
                    title: "주간 스크럼"
                    startTime: "2025-01-26T10:00:00Z"
                    endTime: "2025-01-26T10:30:00Z"
                    location: "온라인"
                    participantCount: 8
                    status: "SCHEDULED"
                activeTodos:
                  - todoId: "660e8400-e29b-41d4-a716-446655440000"
                    content: "API 설계 문서 작성"
                    dueDate: "2025-01-30"
                    priority: "HIGH"
                    status: "IN_PROGRESS"
                    minutesId: "770e8400-e29b-41d4-a716-446655440000"
                  - todoId: "660e8400-e29b-41d4-a716-446655440001"
                    content: "데이터베이스 스키마 설계"
                    dueDate: "2025-02-05"
                    priority: "MEDIUM"
                    status: "IN_PROGRESS"
                    minutesId: "770e8400-e29b-41d4-a716-446655440001"
                myMinutes:
                  - minutesId: "770e8400-e29b-41d4-a716-446655440000"
                    title: "아키텍처 설계 회의"
                    meetingDate: "2025-01-23T14:00:00Z"
                    status: "FINALIZED"
                    participantCount: 6
                    lastModified: "2025-01-23T16:30:00Z"
                statistics:
                  upcomingMeetingsCount: 2
                  activeTodosCount: 5
                  todoCompletionRate: 68.5
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ==================== Meeting APIs ====================
  /meetings:
    post:
      tags:
        - Meeting
      summary: 회의 예약
      description: |
        새로운 회의를 예약하고 참석자를 초대합니다.
        - 회의 정보 저장
        - 참석자 목록 관리
        - 초대 이메일 자동 발송
        - 리마인더 스케줄링
      operationId: createMeeting
      x-user-story: UFR-MEET-010
      x-controller: MeetingController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
        - $ref: '#/components/parameters/UserNameHeader'
        - $ref: '#/components/parameters/UserEmailHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMeetingRequest'
            example:
              title: "Q1 전략 회의"
              startTime: "2025-01-25T14:00:00Z"
              endTime: "2025-01-25T16:00:00Z"
              location: "회의실 A"
              agenda: "1. Q1 목표 달성 현황 검토\n2. Q2 전략 방향 논의\n3. 주요 이슈 및 리스크 검토"
              participants:
                - "user1@example.com"
                - "user2@example.com"
                - "user3@example.com"
      responses:
        '201':
          description: 회의 예약 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeetingResponse'
              example:
                meetingId: "550e8400-e29b-41d4-a716-446655440000"
                title: "Q1 전략 회의"
                startTime: "2025-01-25T14:00:00Z"
                endTime: "2025-01-25T16:00:00Z"
                location: "회의실 A"
                participants:
                  - userId: "user1"
                    email: "user1@example.com"
                    name: "김철수"
                    role: "ORGANIZER"
                  - userId: "user2"
                    email: "user2@example.com"
                    name: "이영희"
                    role: "PARTICIPANT"
                status: "SCHEDULED"
                createdAt: "2025-01-23T10:00:00Z"
        '400':
          $ref: '#/components/responses/BadRequestError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /meetings/{meetingId}/template:
    put:
      tags:
        - Meeting
      summary: 회의록 템플릿 선택
      description: |
        회의에 회의록 템플릿을 적용합니다.
        - 템플릿 유형: 일반 회의, 스크럼, 프로젝트 킥오프, 주간 회의
        - 섹션 커스터마이징 가능
      operationId: applyTemplate
      x-user-story: UFR-MEET-020
      x-controller: MeetingController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MeetingIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApplyTemplateRequest'
            example:
              templateId: "general-meeting"
              customizations:
                - section: "discussion"
                  title: "주요 논의사항"
                  order: 1
                - section: "decision"
                  title: "결정사항"
                  order: 2
      responses:
        '200':
          description: 템플릿 적용 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeetingResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /meetings/{meetingId}/start:
    post:
      tags:
        - Meeting
      summary: 회의 시작
      description: |
        회의를 시작하고 세션을 생성합니다.
        - 회의 세션 생성
        - 음성 녹음 준비
        - 회의록 초안 생성
        - 실시간 협업 WebSocket 준비
      operationId: startMeeting
      x-user-story: UFR-MEET-030
      x-controller: MeetingController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MeetingIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
        - $ref: '#/components/parameters/UserNameHeader'
      responses:
        '201':
          description: 회의 시작 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
              example:
                sessionId: "660e8400-e29b-41d4-a716-446655440000"
                meetingId: "550e8400-e29b-41d4-a716-446655440000"
                status: "IN_PROGRESS"
                startedAt: "2025-01-25T14:00:00Z"
                minutesId: "770e8400-e29b-41d4-a716-446655440000"
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /meetings/{meetingId}/end:
    post:
      tags:
        - Meeting
      summary: 회의 종료
      description: |
        회의를 종료하고 통계를 생성합니다.
        - 음성 녹음 중지
        - 회의 통계 생성
        - AI 제안 데이터 회의록에 반영
        - 참석자에게 종료 알림
        - 검증 완료 화면 데이터 반환
      operationId: endMeeting
      x-user-story: UFR-MEET-040
      x-controller: MeetingController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MeetingIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EndMeetingRequest'
            example:
              finalAgenda: "1. Q1 목표 달성률 85% 확인\n2. 신규 기능 우선순위 결정\n3. 다음 주 개발 일정 조율"
              applyAiSuggestions: true
      responses:
        '200':
          description: 회의 종료 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeetingEndResponse'
              example:
                meetingId: "550e8400-e29b-41d4-a716-446655440000"
                sessionId: "660e8400-e29b-41d4-a716-446655440000"
                status: "ENDED"
                endedAt: "2025-01-25T16:00:00Z"
                statistics:
                  duration: 120
                  participantCount: 5
                  utteranceCount: 87
                minutesId: "770e8400-e29b-41d4-a716-446655440000"
                aiEnhancement:
                  merged: true
                  suggestionsApplied: 8
                  sections:
                    discussion: 3
                    decision: 2
                    todo: 3
                verificationStatus:
                  totalSections: 4
                  verifiedSections: 2
                  verificationRate: 50
                  sections:
                    - sectionId: "880e8400-e29b-41d4-a716-446655440000"
                      sectionType: "DISCUSSION"
                      title: "회의 개요"
                      isVerified: true
                      isLocked: false
                      verifiers:
                        - userId: "user1"
                          name: "김민준"
                          verifiedAt: "2025-01-25T15:30:00Z"
                        - userId: "user2"
                          name: "박서연"
                          verifiedAt: "2025-01-25T15:35:00Z"
                    - sectionId: "880e8400-e29b-41d4-a716-446655440001"
                      sectionType: "DECISION"
                      title: "논의 사항"
                      isVerified: true
                      isLocked: true
                      verifiers:
                        - userId: "user1"
                          name: "김민준"
                          verifiedAt: "2025-01-25T15:40:00Z"
                        - userId: "user2"
                          name: "박서연"
                          verifiedAt: "2025-01-25T15:42:00Z"
                        - userId: "user3"
                          name: "이준호"
                          verifiedAt: "2025-01-25T15:45:00Z"
                    - sectionId: "880e8400-e29b-41d4-a716-446655440002"
                      sectionType: "TODO"
                      title: "결정 사항"
                      isVerified: false
                      isLocked: false
                      verifiers:
                        - userId: "user2"
                          name: "박서연"
                          verifiedAt: "2025-01-25T15:50:00Z"
                    - sectionId: "880e8400-e29b-41d4-a716-446655440003"
                      sectionType: "TODO"
                      title: "액션 아이템"
                      isVerified: false
                      isLocked: false
                      verifiers: []
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  # ==================== Minutes APIs ====================
  /minutes:
    get:
      tags:
        - Minutes
      summary: 회의록 목록 조회
      description: |
        회의록 목록을 조회하고 필터링합니다.
        - 상태별 필터링 (전체/작성중/확정완료)
        - 카테고리별 필터링 (전체/공유받은/참석한/생성한)
        - 정렬 옵션 (최신순/회의일시순/제목순)
        - 검색 기능
      operationId: getMinutesList
      x-user-story: UFR-MEET-046
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
        - name: page
          in: query
          schema:
            type: integer
            default: 0
            minimum: 0
          description: 페이지 번호 (0부터 시작)
        - name: size
          in: query
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: 페이지 크기
        - name: status
          in: query
          schema:
            type: string
            enum: [ALL, DRAFT, FINALIZED]
            default: ALL
          description: 회의록 상태 필터
        - name: category
          in: query
          schema:
            type: string
            enum: [ALL, SHARED, ATTENDED, CREATED]
            default: ALL
          description: 카테고리 필터
        - name: sort
          in: query
          schema:
            type: string
            enum: [LATEST, MEETING_DATE, TITLE]
            default: LATEST
          description: 정렬 옵션
        - name: keyword
          in: query
          schema:
            type: string
          description: 검색 키워드 (제목, 참석자, 키워드)
      responses:
        '200':
          description: 회의록 목록 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MinutesListResponse'
              example:
                content:
                  - minutesId: "770e8400-e29b-41d4-a716-446655440000"
                    title: "아키텍처 설계 회의"
                    meetingDate: "2025-01-23T14:00:00Z"
                    participantCount: 6
                    status: "FINALIZED"
                    verificationRate: 100
                    isReadOnly: false
                    lastModified: "2025-01-23T16:30:00Z"
                  - minutesId: "770e8400-e29b-41d4-a716-446655440001"
                    title: "주간 스프린트 리뷰"
                    meetingDate: "2025-01-22T10:00:00Z"
                    participantCount: 8
                    status: "DRAFT"
                    verificationRate: 65
                    isReadOnly: true
                    lastModified: "2025-01-22T11:30:00Z"
                pageable:
                  page: 0
                  size: 20
                  sort: "LATEST"
                totalElements: 45
                totalPages: 3
                statistics:
                  total: 45
                  draft: 12
                  finalized: 33
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /minutes/{minutesId}:
    get:
      tags:
        - Minutes
      summary: 회의록 상세 조회
      description: |
        회의록의 상세 정보를 조회합니다.
        - 기본 정보 (제목, 일시, 참석자)
        - 섹션별 상세 내용
        - AI 요약 정보
        - 관련 회의록 (최대 3개)
        - 권한 정보
      operationId: getMinutesDetail
      x-user-story: UFR-MEET-047
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
      responses:
        '200':
          description: 회의록 상세 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MinutesDetailResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

    patch:
      tags:
        - Minutes
      summary: 회의록 수정
      description: |
        회의록 내용을 수정합니다.
        - 제목, 섹션별 내용, AI 요약 수정 가능
        - 잠긴 섹션은 수정 불가
        - 확정된 회의록은 작성중으로 상태 변경
        - 실시간 동기화
      operationId: updateMinutes
      x-user-story: UFR-MEET-055
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
        - $ref: '#/components/parameters/UserNameHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMinutesRequest'
      responses:
        '200':
          description: 회의록 수정 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MinutesUpdateResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /minutes/{minutesId}/finalize:
    post:
      tags:
        - Minutes
      summary: 회의록 확정
      description: |
        최종 회의록을 확정하고 버전을 생성합니다.
        - 필수 항목 검증
        - 확정 버전 생성
        - AI Todo 자동 추출
        - 참석자에게 확정 알림
      operationId: finalizeMinutes
      x-user-story: UFR-MEET-050
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
      responses:
        '200':
          description: 회의록 확정 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MinutesFinalizeResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /minutes/{minutesId}/sections/{sectionId}/verify:
    post:
      tags:
        - Minutes
      summary: 섹션 검증 완료
      description: |
        회의록 섹션을 검증하고 완료 표시를 합니다.
        - 검증자 정보 기록
        - 검증 상태 업데이트
        - 실시간 동기화
        - 전체 메일 알림
      operationId: verifySection
      x-user-story: UFR-COLLAB-030
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - name: sectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: 섹션 ID
        - $ref: '#/components/parameters/UserIdHeader'
        - $ref: '#/components/parameters/UserNameHeader'
      responses:
        '200':
          description: 섹션 검증 완료
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SectionVerifyResponse'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /minutes/{minutesId}/sections/{sectionId}/lock:
    post:
      tags:
        - Minutes
      summary: 섹션 잠금
      description: |
        검증 완료된 섹션을 잠급니다. (회의 생성자만 가능)
        - 잠긴 섹션은 수정 불가
        - 실시간 동기화
      operationId: lockSection
      x-user-story: UFR-COLLAB-030
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - name: sectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/UserIdHeader'
      responses:
        '200':
          description: 섹션 잠금 성공
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

    delete:
      tags:
        - Minutes
      summary: 섹션 잠금 해제
      description: |
        잠긴 섹션의 잠금을 해제합니다. (회의 생성자만 가능)
      operationId: unlockSection
      x-user-story: UFR-MEET-055
      x-controller: MinutesController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - name: sectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/UserIdHeader'
      responses:
        '200':
          description: 섹션 잠금 해제 성공
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  # ==================== Todo APIs ====================
  /todos:
    get:
      tags:
        - Todo
      summary: Todo 목록 조회
      description: |
        사용자별 Todo 목록을 조회하고 필터링합니다.
        - 상태별 필터링 (전체/진행중/완료/마감임박)
        - 우선순위별 정렬
        - 통계 정보 포함
      operationId: getTodoList
      x-user-story: UFR-TODO-020
      x-controller: TodoController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
        - name: status
          in: query
          schema:
            type: string
            enum: [ALL, IN_PROGRESS, COMPLETED, URGENT]
            default: ALL
          description: |
            Todo 상태 필터
            - ALL: 전체
            - IN_PROGRESS: 진행중
            - COMPLETED: 완료
            - URGENT: 마감 임박 (3일 이내)
        - name: sort
          in: query
          schema:
            type: string
            enum: [DUE_DATE, PRIORITY, CREATED_AT]
            default: DUE_DATE
          description: 정렬 기준
      responses:
        '200':
          description: Todo 목록 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoListResponse'
              example:
                statistics:
                  total: 12
                  inProgress: 8
                  completed: 4
                  urgent: 3
                  completionRate: 33.3
                todos:
                  - todoId: "660e8400-e29b-41d4-a716-446655440000"
                    content: "API 설계 문서 작성"
                    assignee:
                      userId: "user1"
                      email: "user@example.com"
                      name: "김민준"
                    dueDate: "2025-01-30"
                    priority: "HIGH"
                    status: "IN_PROGRESS"
                    progress: 60
                    minutesId: "770e8400-e29b-41d4-a716-446655440000"
                    meetingTitle: "아키텍처 설계 회의"
                    createdAt: "2025-01-23T10:00:00Z"
                  - todoId: "660e8400-e29b-41d4-a716-446655440001"
                    content: "데이터베이스 스키마 설계"
                    assignee:
                      userId: "user1"
                      email: "user@example.com"
                      name: "김민준"
                    dueDate: "2025-02-05"
                    priority: "MEDIUM"
                    status: "IN_PROGRESS"
                    progress: 30
                    minutesId: "770e8400-e29b-41d4-a716-446655440001"
                    meetingTitle: "주간 스프린트 리뷰"
                    createdAt: "2025-01-22T14:00:00Z"
        '401':
          $ref: '#/components/responses/UnauthorizedError'

    post:
      tags:
        - Todo
      summary: Todo 할당
      description: |
        Todo를 생성하고 담당자에게 할당합니다.
        - 회의록과 양방향 연결
        - 담당자에게 즉시 알림 발송
        - 캘린더 이벤트 생성
        - 리마인더 설정
      operationId: createTodo
      x-user-story: UFR-TODO-010
      x-controller: TodoController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/UserIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTodoRequest'
            example:
              content: "API 설계 문서 작성"
              assignee: "user@example.com"
              dueDate: "2025-01-30"
              priority: "HIGH"
              minutesId: "770e8400-e29b-41d4-a716-446655440000"
              sectionId: "880e8400-e29b-41d4-a716-446655440000"
      responses:
        '201':
          description: Todo 생성 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /todos/{todoId}/complete:
    patch:
      tags:
        - Todo
      summary: Todo 완료 처리
      description: |
        Todo를 완료 상태로 변경합니다.
        - 담당자만 완료 가능
        - 회의록에 완료 상태 실시간 반영
        - 완료 알림 발송
        - 모든 Todo 완료 시 전체 완료 알림
      operationId: completeTodo
      x-user-story: UFR-TODO-030
      x-controller: TodoController
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/TodoIdPath'
        - $ref: '#/components/parameters/UserIdHeader'
      responses:
        '200':
          description: Todo 완료 처리 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoCompleteResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  # ==================== Template APIs ====================
  /templates:
    get:
      tags:
        - Template
      summary: 템플릿 목록 조회
      description: |
        사용 가능한 회의록 템플릿 목록을 조회합니다.
        각 템플릿의 섹션 정보를 포함하여 반환합니다.
        - 일반 회의
        - 스크럼 회의
        - 프로젝트 킥오프
        - 주간 회의
      operationId: getTemplates
      x-user-story: UFR-MEET-020
      x-controller: TemplateController
      security:
        - bearerAuth: []
      responses:
        '200':
          description: 템플릿 목록 조회 성공
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TemplateResponse'
              example:
                - templateId: "general-meeting"
                  name: "일반 회의"
                  description: "기본 회의록 형식"
                  icon: "📋"
                  category: "GENERAL"
                  sections:
                    - sectionType: "DISCUSSION"
                      title: "회의 개요"
                      order: 1
                      isRequired: true
                    - sectionType: "DISCUSSION"
                      title: "논의 사항"
                      order: 2
                      isRequired: true
                    - sectionType: "DECISION"
                      title: "결정 사항"
                      order: 3
                      isRequired: true
                    - sectionType: "TODO"
                      title: "액션 아이템"
                      order: 4
                      isRequired: true
                - templateId: "scrum-meeting"
                  name: "스크럼 회의"
                  description: "데일리 스탠드업 형식"
                  icon: "🏃"
                  category: "SCRUM"
                  sections:
                    - sectionType: "DISCUSSION"
                      title: "어제 한 일"
                      order: 1
                      isRequired: true
                    - sectionType: "TODO"
                      title: "오늘 할 일"
                      order: 2
                      isRequired: true
                    - sectionType: "CUSTOM"
                      title: "블로커/이슈"
                      order: 3
                      isRequired: true

  # ==================== WebSocket Endpoints ====================
  /ws/minutes/{minutesId}:
    get:
      tags:
        - WebSocket
      summary: 회의록 실시간 협업 WebSocket
      description: |
        WebSocket 연결을 통한 실시간 협업 기능
        - 회의록 실시간 수정 동기화
        - 충돌 감지 및 해결
        - 검증 완료 상태 동기화
        - Todo 상태 변경 동기화

        **연결 URL:** ws://localhost:8080/ws/minutes/{minutesId}

        **메시지 타입:**
        - MINUTES_UPDATED: 회의록 수정
        - TODO_COMPLETED: Todo 완료
        - SECTION_VERIFIED: 섹션 검증 완료
        - CONFLICT_DETECTED: 충돌 감지
      operationId: connectMinutesWebSocket
      x-user-story: UFR-COLLAB-010
      x-controller: CollaborationController
      parameters:
        - $ref: '#/components/parameters/MinutesIdPath'
        - name: userId
          in: query
          required: true
          schema:
            type: string
          description: 사용자 ID
        - name: token
          in: query
          required: true
          schema:
            type: string
          description: JWT 토큰
      responses:
        '101':
          description: WebSocket 연결 성공
        '401':
          description: 인증 실패
        '403':
          description: 권한 없음

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT 토큰을 Authorization 헤더에 포함

  parameters:
    UserIdHeader:
      name: X-User-Id
      in: header
      required: true
      schema:
        type: string
      description: 사용자 ID

    UserNameHeader:
      name: X-User-Name
      in: header
      required: true
      schema:
        type: string
      description: 사용자 이름

    UserEmailHeader:
      name: X-User-Email
      in: header
      required: true
      schema:
        type: string
        format: email
      description: 사용자 이메일

    MeetingIdPath:
      name: meetingId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: 회의 ID

    MinutesIdPath:
      name: minutesId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: 회의록 ID

    TodoIdPath:
      name: todoId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Todo ID

  schemas:
    # ==================== Dashboard Schemas ====================
    DashboardResponse:
      type: object
      properties:
        upcomingMeetings:
          type: array
          items:
            $ref: '#/components/schemas/UpcomingMeeting'
          description: 예정된 회의 목록
        activeTodos:
          type: array
          items:
            $ref: '#/components/schemas/ActiveTodo'
          description: 진행 중 Todo 목록
        myMinutes:
          type: array
          items:
            $ref: '#/components/schemas/MyMinutes'
          description: 내 회의록 목록
        statistics:
          $ref: '#/components/schemas/DashboardStatistics'
          description: 통계 정보
      required:
        - statistics

    UpcomingMeeting:
      type: object
      properties:
        meetingId:
          type: string
          format: uuid
        title:
          type: string
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        location:
          type: string
        participantCount:
          type: integer
        status:
          type: string
          enum: [SCHEDULED, IN_PROGRESS]
      required:
        - meetingId
        - title
        - startTime
        - status

    ActiveTodo:
      type: object
      properties:
        todoId:
          type: string
          format: uuid
        content:
          type: string
        dueDate:
          type: string
          format: date
        priority:
          type: string
          enum: [HIGH, MEDIUM, LOW]
        status:
          type: string
          enum: [IN_PROGRESS]
        minutesId:
          type: string
          format: uuid
      required:
        - todoId
        - content
        - priority
        - status

    MyMinutes:
      type: object
      properties:
        minutesId:
          type: string
          format: uuid
        title:
          type: string
        meetingDate:
          type: string
          format: date-time
        status:
          type: string
          enum: [DRAFT, FINALIZED]
        participantCount:
          type: integer
        lastModified:
          type: string
          format: date-time
      required:
        - minutesId
        - title
        - meetingDate
        - status

    DashboardStatistics:
      type: object
      properties:
        upcomingMeetingsCount:
          type: integer
        activeTodosCount:
          type: integer
        todoCompletionRate:
          type: number
          format: double
          description: Todo 완료율 (%)
      required:
        - upcomingMeetingsCount
        - activeTodosCount
        - todoCompletionRate

    # ==================== Meeting Schemas ====================
    CreateMeetingRequest:
      type: object
      properties:
        title:
          type: string
          maxLength: 100
          description: 회의 제목
        startTime:
          type: string
          format: date-time
          description: 회의 시작 시간
        endTime:
          type: string
          format: date-time
          description: 회의 종료 시간
        location:
          type: string
          maxLength: 200
          description: 회의 장소
        agenda:
          type: string
          maxLength: 1000
          description: 회의 안건 (선택 사항)
        participants:
          type: array
          items:
            type: string
            format: email
          minItems: 1
          description: 참석자 이메일 목록
      required:
        - title
        - startTime
        - endTime
        - participants

    MeetingResponse:
      type: object
      properties:
        meetingId:
          type: string
          format: uuid
        title:
          type: string
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        location:
          type: string
        participants:
          type: array
          items:
            $ref: '#/components/schemas/Participant'
        status:
          type: string
          enum: [SCHEDULED, IN_PROGRESS, ENDED]
        createdAt:
          type: string
          format: date-time
      required:
        - meetingId
        - title
        - startTime
        - endTime
        - participants
        - status

    Participant:
      type: object
      properties:
        userId:
          type: string
        email:
          type: string
          format: email
        name:
          type: string
        role:
          type: string
          enum: [ORGANIZER, PARTICIPANT]
      required:
        - userId
        - email
        - name
        - role

    ApplyTemplateRequest:
      type: object
      properties:
        templateId:
          type: string
          description: 템플릿 ID
        customizations:
          type: array
          items:
            $ref: '#/components/schemas/SectionCustomization'
      required:
        - templateId

    SectionCustomization:
      type: object
      properties:
        section:
          type: string
        title:
          type: string
        order:
          type: integer
      required:
        - section
        - title
        - order

    SessionResponse:
      type: object
      properties:
        sessionId:
          type: string
          format: uuid
        meetingId:
          type: string
          format: uuid
        status:
          type: string
          enum: [IN_PROGRESS]
        startedAt:
          type: string
          format: date-time
        minutesId:
          type: string
          format: uuid
      required:
        - sessionId
        - meetingId
        - status
        - startedAt
        - minutesId

    MeetingEndResponse:
      type: object
      properties:
        meetingId:
          type: string
          format: uuid
        sessionId:
          type: string
          format: uuid
        status:
          type: string
          enum: [ENDED]
        endedAt:
          type: string
          format: date-time
        statistics:
          $ref: '#/components/schemas/MeetingStatistics'
        minutesId:
          type: string
          format: uuid
        aiEnhancement:
          $ref: '#/components/schemas/AIEnhancement'
        verificationStatus:
          $ref: '#/components/schemas/VerificationStatus'
      required:
        - meetingId
        - sessionId
        - status
        - endedAt
        - statistics
        - minutesId

    MeetingStatistics:
      type: object
      properties:
        duration:
          type: integer
          description: 회의 진행 시간 (분)
        participantCount:
          type: integer
          description: 참석자 수
        utteranceCount:
          type: integer
          description: 발언 횟수
      required:
        - duration
        - participantCount
        - utteranceCount

    AIEnhancement:
      type: object
      properties:
        merged:
          type: boolean
          description: AI 제안 병합 여부
        suggestionsApplied:
          type: integer
          description: 적용된 AI 제안 수
        sections:
          type: object
          properties:
            discussion:
              type: integer
            decision:
              type: integer
            todo:
              type: integer
      required:
        - merged
        - suggestionsApplied

    EndMeetingRequest:
      type: object
      properties:
        finalAgenda:
          type: string
          description: 최종 회의 안건
        applyAiSuggestions:
          type: boolean
          default: true
          description: AI 제안 자동 적용 여부

    VerificationStatus:
      type: object
      properties:
        totalSections:
          type: integer
          description: 전체 섹션 수
        verifiedSections:
          type: integer
          description: 검증 완료된 섹션 수
        verificationRate:
          type: integer
          description: 검증 완료율 (%)
        sections:
          type: array
          items:
            $ref: '#/components/schemas/SectionVerificationInfo'
          description: 섹션별 검증 상태
      required:
        - totalSections
        - verifiedSections
        - verificationRate
        - sections

    SectionVerificationInfo:
      type: object
      properties:
        sectionId:
          type: string
          format: uuid
        sectionType:
          type: string
          enum: [DISCUSSION, DECISION, TODO, SCHEDULE, RESOURCE, CUSTOM]
        title:
          type: string
        isVerified:
          type: boolean
        isLocked:
          type: boolean
        verifiers:
          type: array
          items:
            $ref: '#/components/schemas/Verifier'
      required:
        - sectionId
        - sectionType
        - title
        - isVerified
        - isLocked
        - verifiers

    Verifier:
      type: object
      properties:
        userId:
          type: string
        name:
          type: string
        verifiedAt:
          type: string
          format: date-time
      required:
        - userId
        - name
        - verifiedAt

    # ==================== Minutes Schemas ====================
    MinutesListResponse:
      type: object
      properties:
        content:
          type: array
          items:
            $ref: '#/components/schemas/MinutesSummary'
        pageable:
          $ref: '#/components/schemas/Pageable'
        totalElements:
          type: integer
        totalPages:
          type: integer
        statistics:
          $ref: '#/components/schemas/MinutesStatistics'
      required:
        - content
        - pageable
        - totalElements
        - totalPages
        - statistics

    MinutesSummary:
      type: object
      properties:
        minutesId:
          type: string
          format: uuid
        title:
          type: string
        meetingDate:
          type: string
          format: date-time
        participantCount:
          type: integer
        status:
          type: string
          enum: [DRAFT, FINALIZED]
        verificationRate:
          type: integer
          description: 검증 완료율 (%)
        isReadOnly:
          type: boolean
          description: 조회 전용 여부
        lastModified:
          type: string
          format: date-time
      required:
        - minutesId
        - title
        - meetingDate
        - status

    Pageable:
      type: object
      properties:
        page:
          type: integer
        size:
          type: integer
        sort:
          type: string
      required:
        - page
        - size
        - sort

    MinutesStatistics:
      type: object
      properties:
        total:
          type: integer
        draft:
          type: integer
        finalized:
          type: integer
      required:
        - total
        - draft
        - finalized

    MinutesDetailResponse:
      type: object
      properties:
        minutesId:
          type: string
          format: uuid
        meeting:
          $ref: '#/components/schemas/MeetingInfo'
        participants:
          type: array
          items:
            $ref: '#/components/schemas/Participant'
        sections:
          type: array
          items:
            $ref: '#/components/schemas/MinutesSection'
          description: 섹션별 내용 (AI 요약 및 관련 회의록 포함)
        permissions:
          $ref: '#/components/schemas/MinutesPermissions'
        status:
          type: string
          enum: [DRAFT, FINALIZED]
        version:
          type: string
      required:
        - minutesId
        - meeting
        - participants
        - sections
        - permissions
        - status

    MeetingInfo:
      type: object
      properties:
        meetingId:
          type: string
          format: uuid
        title:
          type: string
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        location:
          type: string
        duration:
          type: integer
          description: 소요 시간 (분)
      required:
        - meetingId
        - title
        - startTime

    MinutesSection:
      type: object
      properties:
        sectionId:
          type: string
          format: uuid
        sectionType:
          type: string
          enum: [DISCUSSION, DECISION, TODO, SCHEDULE, RESOURCE, CUSTOM]
        title:
          type: string
        content:
          type: string
        order:
          type: integer
        isLocked:
          type: boolean
        isVerified:
          type: boolean
        verifiedBy:
          type: string
        verifiedAt:
          type: string
          format: date-time
        aiSummary:
          $ref: '#/components/schemas/AISummary'
          description: 이 섹션의 AI 요약
        relatedMinutes:
          type: array
          items:
            $ref: '#/components/schemas/RelatedMinutes'
          description: 이 섹션과 관련된 회의록 목록 (최대 3개)
      required:
        - sectionId
        - sectionType
        - title
        - content
        - order
        - isLocked
        - isVerified

    AISummary:
      type: object
      properties:
        summaryId:
          type: string
          format: uuid
        content:
          type: string
          description: AI가 생성한 요약 내용
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      required:
        - summaryId
        - content

    RelatedMinutes:
      type: object
      properties:
        minutesId:
          type: string
          format: uuid
        title:
          type: string
        meetingDate:
          type: string
          format: date-time
        relevanceScore:
          type: number
          format: double
          description: 관련도 점수 (0-100%)
        summary:
          type: string
      required:
        - minutesId
        - title
        - meetingDate
        - relevanceScore

    MinutesPermissions:
      type: object
      properties:
        canEdit:
          type: boolean
        canShare:
          type: boolean
      required:
        - canEdit
        - canShare

    UpdateMinutesRequest:
      type: object
      properties:
        title:
          type: string
        sections:
          type: array
          items:
            $ref: '#/components/schemas/SectionUpdate'

    SectionUpdate:
      type: object
      properties:
        sectionId:
          type: string
          format: uuid
        content:
          type: string
        aiSummary:
          type: string
        relatedMinutesIds:
          type: array
          items:
            type: string
            format: uuid
          description: 이 섹션과 연결할 관련 회의록 ID 목록 (최대 3개)
          maxItems: 3
      required:
        - sectionId

    MinutesUpdateResponse:
      type: object
      properties:
        minutesId:
          type: string
          format: uuid
        status:
          type: string
          enum: [DRAFT, FINALIZED]
        updatedAt:
          type: string
          format: date-time
        updatedBy:
          type: string
        version:
          type: string
      required:
        - minutesId
        - status
        - updatedAt
        - updatedBy

    MinutesFinalizeResponse:
      type: object
      properties:
        minutesId:
          type: string
          format: uuid
        status:
          type: string
          enum: [FINALIZED]
        version:
          type: string
        finalizedAt:
          type: string
          format: date-time
        finalizedBy:
          type: string
      required:
        - minutesId
        - status
        - version
        - finalizedAt
        - finalizedBy

    SectionVerifyResponse:
      type: object
      properties:
        sectionId:
          type: string
          format: uuid
        isVerified:
          type: boolean
        verifiedBy:
          type: string
        verifiedAt:
          type: string
          format: date-time
      required:
        - sectionId
        - isVerified

    # ==================== Todo Schemas ====================
    TodoListResponse:
      type: object
      properties:
        statistics:
          $ref: '#/components/schemas/TodoStatistics'
        todos:
          type: array
          items:
            $ref: '#/components/schemas/TodoDetail'
      required:
        - statistics
        - todos

    TodoStatistics:
      type: object
      properties:
        total:
          type: integer
          description: 전체 Todo 개수
        inProgress:
          type: integer
          description: 진행 중 Todo 개수
        completed:
          type: integer
          description: 완료된 Todo 개수
        urgent:
          type: integer
          description: 마감 임박 Todo 개수 (3일 이내)
        completionRate:
          type: number
          format: double
          description: 완료율 (%)
      required:
        - total
        - inProgress
        - completed
        - urgent
        - completionRate

    TodoDetail:
      type: object
      properties:
        todoId:
          type: string
          format: uuid
        content:
          type: string
        assignee:
          $ref: '#/components/schemas/TodoAssignee'
        dueDate:
          type: string
          format: date
        priority:
          type: string
          enum: [HIGH, MEDIUM, LOW]
        status:
          type: string
          enum: [IN_PROGRESS, COMPLETED]
        progress:
          type: integer
          minimum: 0
          maximum: 100
          description: 진행률 (%)
        minutesId:
          type: string
          format: uuid
        meetingTitle:
          type: string
          description: 관련 회의 제목
        createdAt:
          type: string
          format: date-time
      required:
        - todoId
        - content
        - assignee
        - priority
        - status
        - minutesId
        - meetingTitle

    TodoAssignee:
      type: object
      properties:
        userId:
          type: string
        email:
          type: string
          format: email
        name:
          type: string
      required:
        - userId
        - email
        - name

    CreateTodoRequest:
      type: object
      properties:
        content:
          type: string
          maxLength: 500
        assignee:
          type: string
          format: email
        dueDate:
          type: string
          format: date
        priority:
          type: string
          enum: [HIGH, MEDIUM, LOW]
          default: MEDIUM
        minutesId:
          type: string
          format: uuid
        sectionId:
          type: string
          format: uuid
      required:
        - content
        - assignee
        - minutesId

    TodoResponse:
      type: object
      properties:
        todoId:
          type: string
          format: uuid
        content:
          type: string
        assignee:
          type: string
          format: email
        dueDate:
          type: string
          format: date
        priority:
          type: string
          enum: [HIGH, MEDIUM, LOW]
        status:
          type: string
          enum: [IN_PROGRESS, COMPLETED]
        minutesId:
          type: string
          format: uuid
        sectionId:
          type: string
          format: uuid
        calendarEventId:
          type: string
        createdAt:
          type: string
          format: date-time
      required:
        - todoId
        - content
        - assignee
        - priority
        - status
        - minutesId

    TodoCompleteResponse:
      type: object
      properties:
        todoId:
          type: string
          format: uuid
        status:
          type: string
          enum: [COMPLETED]
        completedAt:
          type: string
          format: date-time
        completedBy:
          type: string
        minutesId:
          type: string
          format: uuid
        allTodosCompleted:
          type: boolean
      required:
        - todoId
        - status
        - completedAt
        - completedBy
        - allTodosCompleted

    # ==================== Template Schemas ====================
    TemplateResponse:
      type: object
      properties:
        templateId:
          type: string
        name:
          type: string
        description:
          type: string
        icon:
          type: string
          description: 템플릿 아이콘 (이모지)
        category:
          type: string
          enum: [GENERAL, SCRUM, KICKOFF, WEEKLY]
        sections:
          type: array
          items:
            $ref: '#/components/schemas/TemplateSection'
          description: 템플릿 섹션 목록
      required:
        - templateId
        - name
        - category
        - sections

    TemplateSection:
      type: object
      properties:
        sectionType:
          type: string
        title:
          type: string
        order:
          type: integer
        isRequired:
          type: boolean
      required:
        - sectionType
        - title
        - order
        - isRequired

    # ==================== Error Schemas ====================
    ErrorResponse:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error

    Error:
      type: object
      properties:
        code:
          type: string
          description: 에러 코드
        message:
          type: string
          description: 에러 메시지
        details:
          type: string
          description: 상세 설명
        timestamp:
          type: string
          format: date-time
          description: 발생 시간
        path:
          type: string
          description: 요청 경로
      required:
        - code
        - message
        - timestamp

  responses:
    BadRequestError:
      description: 잘못된 요청
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: "BAD_REQUEST"
              message: "잘못된 요청입니다"
              details: "요청 데이터의 형식이 올바르지 않습니다"
              timestamp: "2025-10-23T12:00:00Z"
              path: "/api/meetings"

    UnauthorizedError:
      description: 인증 실패
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: "UNAUTHORIZED"
              message: "인증이 필요합니다"
              details: "유효한 JWT 토큰을 제공해주세요"
              timestamp: "2025-10-23T12:00:00Z"
              path: "/api/dashboard"

    ForbiddenError:
      description: 권한 없음
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: "FORBIDDEN"
              message: "권한이 없습니다"
              details: "해당 리소스에 접근할 권한이 없습니다"
              timestamp: "2025-10-23T12:00:00Z"
              path: "/api/minutes/{minutesId}"

    NotFoundError:
      description: 리소스를 찾을 수 없음
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: "NOT_FOUND"
              message: "리소스를 찾을 수 없습니다"
              details: "요청한 리소스가 존재하지 않습니다"
              timestamp: "2025-10-23T12:00:00Z"
              path: "/api/meetings/{meetingId}"

    ConflictError:
      description: 충돌 발생
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: "CONFLICT"
              message: "충돌이 발생했습니다"
              details: "중복된 회의 시간이 존재합니다"
              timestamp: "2025-10-23T12:00:00Z"
              path: "/api/meetings"

    InternalServerError:
      description: 서버 내부 오류
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: "INTERNAL_SERVER_ERROR"
              message: "서버 내부 오류가 발생했습니다"
              details: "잠시 후 다시 시도해주세요"
              timestamp: "2025-10-23T12:00:00Z"
              path: "/api/dashboard"

security:
  - bearerAuth: []
