@startuml participation-당첨자추첨
!theme mono

title Participation Service - 당첨자 추첨 내부 시퀀스

actor "사장님" as Owner
participant "API Gateway" as Gateway
participant "ParticipationController" as Controller
participant "ParticipationService" as Service
participant "LotteryAlgorithm" as Lottery
participant "ParticipantRepository" as Repo
participant "DrawLogRepository" as LogRepo
database "Participation DB" as DB

== UFR-PART-030: 당첨자 추첨 ==

Owner -> Gateway: POST /api/v1/events/{eventId}/draw-winners\n{winnerCount, visitBonus, algorithm}
activate Gateway

Gateway -> Gateway: JWT 토큰 검증\n- 토큰 유효성 확인\n- 사장님 권한 확인

alt JWT 검증 실패
    Gateway --> Owner: 401 Unauthorized
    deactivate Gateway
else JWT 검증 성공

    Gateway -> Controller: POST /participations/draw-winners\n{eventId, winnerCount, visitBonus}
    activate Controller

    Controller -> Controller: 요청 데이터 유효성 검증\n- eventId 필수\n- winnerCount > 0\n- winnerCount <= 참여자 수

    alt 유효성 검증 실패
        Controller --> Gateway: 400 Bad Request
        Gateway --> Owner: 400 Bad Request
        deactivate Controller
        deactivate Gateway
    else 유효성 검증 성공

        Controller -> Service: drawWinners(eventId, winnerCount, visitBonus)
        activate Service

        Service -> Service: 이벤트 상태 확인\n- 이벤트 종료 여부\n- 이미 추첨 완료 여부

        Service -> LogRepo: findByEventId(eventId)
        activate LogRepo
        LogRepo -> DB: 추첨 로그 조회\n(이벤트ID로 조회)
        activate DB
        DB --> LogRepo: 추첨 로그 조회
        deactivate DB
        LogRepo --> Service: Optional<DrawLog>
        deactivate LogRepo

        alt 이미 추첨 완료
            Service --> Controller: AlreadyDrawnException
            Controller --> Gateway: 409 Conflict\n{message: "이미 추첨이 완료된 이벤트입니다"}
            Gateway --> Owner: 409 Conflict
            deactivate Service
            deactivate Controller
            deactivate Gateway
        else 추첨 가능 상태

            Service -> Repo: findAllByEventIdAndIsWinner(eventId, false)
            activate Repo
            Repo -> DB: 미당첨 참여자 목록 조회\n(이벤트ID로 당첨되지 않은\n참여자 전체 조회,\n참여일시 오름차순 정렬)
            activate DB
            DB --> Repo: 전체 참여자 목록
            deactivate DB
            Repo --> Service: List<Participant>
            deactivate Repo

            alt 참여자 수 부족
            Service --> Controller: InsufficientParticipantsException
            Controller --> Gateway: 400 Bad Request\n{message: "참여자 수가 부족합니다"}
            Gateway --> Owner: 400 Bad Request
            deactivate Service
            deactivate Controller
            deactivate Gateway
        else 추첨 진행

                Service -> Lottery: executeLottery(participants, winnerCount, visitBonus)
                activate Lottery

                note right of Lottery
                    추첨 알고리즘:
                    시간 복잡도: O(n log n)
                    공간 복잡도: O(n)

                    1. 난수 생성 (Crypto.randomBytes)
                    2. 매장 방문 가산점 적용 (옵션)
                       - 방문 고객: 가중치 2배
                       - 비방문 고객: 가중치 1배
                    3. Fisher-Yates Shuffle
                       - 가중치 기반 확률 분포
                       - 무작위 섞기
                    4. 상위 N명 선정
                end note

                Lottery -> Lottery: Step 1: 난수 시드 생성\n- Crypto.randomBytes(32)\n- 예측 불가능한 난수 보장

                Lottery -> Lottery: Step 2: 가산점 적용\n- visitBonus = true일 경우\n- 매장 방문 경로 참여자 가중치 증가

                Lottery -> Lottery: Step 3: Fisher-Yates Shuffle\n- 가중치 기반 확률 분포\n- O(n) 시간 복잡도

                Lottery -> Lottery: Step 4: 당첨자 선정\n- 상위 winnerCount명 추출

                Lottery --> Service: List<Participant> 당첨자 목록
                deactivate Lottery

                Service -> Service: DB 트랜잭션 시작

                alt DB 저장 실패 시
                    note right of Service
                        트랜잭션 롤백 처리:
                        - 당첨자 업데이트 취소
                        - 추첨 로그 저장 취소
                        - 재시도 가능 상태 유지
                    end note
                end

                Service -> Repo: updateWinners(winnerIds)
                activate Repo
                Repo -> DB: 당첨자 정보 업데이트\n(당첨 여부를 true로,\n당첨 일시를 현재 시각으로 설정,\n대상: 선정된 참여자ID 목록)
                activate DB
                DB --> Repo: 업데이트 완료
                deactivate DB
                Repo --> Service: 업데이트 건수
                deactivate Repo

                Service -> Service: DrawLog 엔티티 생성\n- drawLogId (UUID)\n- eventId\n- drawMethod: "RANDOM"\n- algorithm: "FISHER_YATES_SHUFFLE"\n- visitBonusApplied\n- winnerCount\n- drawnAt (현재시각)

                Service -> LogRepo: save(drawLog)
                activate LogRepo
                LogRepo -> DB: 추첨 로그 저장\n(추첨로그ID, 이벤트ID,\n추첨방법, 알고리즘,\n가산점적용여부, 당첨인원,\n추첨일시 저장)
                activate DB
                note right of DB
                    추첨 로그 저장:
                    - 추첨 일시 기록
                    - 알고리즘 버전 기록
                    - 가산점 적용 여부
                    - 감사 추적 목적
                end note
                DB --> LogRepo: 로그 저장 완료
                deactivate DB
                LogRepo --> Service: DrawLog 엔티티
                deactivate LogRepo

                Service -> Service: DB 트랜잭션 커밋

                Service --> Controller: DrawWinnersResponse\n{당첨자목록, 추첨로그ID}
                deactivate Service

                Controller --> Gateway: 200 OK\n{winners[], drawLogId, message}
                deactivate Controller

                Gateway --> Owner: 200 OK
                deactivate Gateway

                Owner -> Owner: 당첨자 목록 화면 표시\n- 당첨자 정보 테이블\n- 재추첨 버튼\n- 추첨 완료 메시지
        end
        end
    end
end

@enduml
