package com.unicorn.hgzero.notification.domain;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.Comment;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 알림 Entity
 *
 * 알림 발송 이력을 관리하는 엔티티
 * 회의 초대, Todo 할당 등 다양한 알림 유형을 지원
 *
 * @author 준호 (Backend Developer)
 * @version 1.0
 * @since 2025-10-23
 */
@Entity
@Table(name = "notifications", indexes = {
    @Index(name = "idx_notification_reference", columnList = "reference_id, reference_type"),
    @Index(name = "idx_notification_created_at", columnList = "created_at")
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Comment("알림 정보")
public class Notification {

    /**
     * 알림 고유 ID (UUID)
     */
    @Id
    @Column(name = "notification_id", length = 36, nullable = false)
    @Comment("알림 고유 ID")
    private String notificationId;

    /**
     * 이벤트 고유 ID (중복 발송 방지용)
     */
    @Column(name = "event_id", length = 100, nullable = false, unique = true)
    @Comment("이벤트 고유 ID (Idempotency)")
    private String eventId;

    /**
     * 참조 대상 ID (meetingId 또는 todoId)
     */
    @Column(name = "reference_id", length = 36, nullable = false)
    @Comment("참조 대상 ID (회의 또는 Todo)")
    private String referenceId;

    /**
     * 참조 유형 (MEETING, TODO)
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "reference_type", length = 20, nullable = false)
    @Comment("참조 유형")
    private ReferenceType referenceType;

    /**
     * 알림 유형 (INVITATION, TODO_ASSIGNED, REMINDER)
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "notification_type", length = 30, nullable = false)
    @Comment("알림 유형")
    private NotificationType notificationType;

    /**
     * 알림 제목
     */
    @Column(name = "title", length = 500, nullable = false)
    @Comment("알림 제목")
    private String title;

    /**
     * 알림 내용 (간단한 요약)
     */
    @Column(name = "message", columnDefinition = "TEXT")
    @Comment("알림 내용")
    private String message;

    /**
     * 알림 상태 (PENDING, PROCESSING, SENT, FAILED, PARTIAL)
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "status", length = 20, nullable = false)
    @Comment("알림 상태")
    private NotificationStatus status;

    /**
     * 발송 채널 (EMAIL, SMS, PUSH)
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "channel", length = 20, nullable = false)
    @Comment("발송 채널")
    private NotificationChannel channel;

    /**
     * 발송 완료 건수
     */
    @Column(name = "sent_count", nullable = false)
    @Comment("발송 완료 건수")
    @Builder.Default
    private Integer sentCount = 0;

    /**
     * 발송 실패 건수
     */
    @Column(name = "failed_count", nullable = false)
    @Comment("발송 실패 건수")
    @Builder.Default
    private Integer failedCount = 0;

    /**
     * 생성 일시
     */
    @Column(name = "created_at", nullable = false, updatable = false)
    @Comment("생성 일시")
    private LocalDateTime createdAt;

    /**
     * 발송 완료 일시
     */
    @Column(name = "sent_at")
    @Comment("발송 완료 일시")
    private LocalDateTime sentAt;

    /**
     * 수신자 목록
     *
     * Cascade: ALL - 알림 삭제 시 수신자 정보도 함께 삭제
     * Orphan Removal: true - 수신자 목록에서 제거 시 DB에서도 삭제
     */
    @OneToMany(mappedBy = "notification", cascade = CascadeType.ALL, orphanRemoval = true)
    @Builder.Default
    private List<NotificationRecipient> recipients = new ArrayList<>();

    /**
     * 생성 전 초기화
     *
     * - notificationId: UUID 생성
     * - createdAt: 현재 시각
     * - status: PENDING
     */
    @PrePersist
    protected void onCreate() {
        if (this.notificationId == null) {
            this.notificationId = UUID.randomUUID().toString();
        }
        if (this.createdAt == null) {
            this.createdAt = LocalDateTime.now();
        }
        if (this.status == null) {
            this.status = NotificationStatus.PENDING;
        }
    }

    /**
     * 수신자 추가 헬퍼 메서드
     *
     * @param recipient 수신자 정보
     */
    public void addRecipient(NotificationRecipient recipient) {
        recipients.add(recipient);
        recipient.setNotification(this);
    }

    /**
     * 수신자 제거 헬퍼 메서드
     *
     * @param recipient 수신자 정보
     */
    public void removeRecipient(NotificationRecipient recipient) {
        recipients.remove(recipient);
        recipient.setNotification(null);
    }

    /**
     * 알림 상태 업데이트
     *
     * @param status 새로운 상태
     */
    public void updateStatus(NotificationStatus status) {
        this.status = status;
        if (status == NotificationStatus.SENT || status == NotificationStatus.PARTIAL) {
            this.sentAt = LocalDateTime.now();
        }
    }

    /**
     * 발송 건수 증가
     */
    public void incrementSentCount() {
        this.sentCount++;
    }

    /**
     * 실패 건수 증가
     */
    public void incrementFailedCount() {
        this.failedCount++;
    }

    /**
     * 참조 유형 Enum
     */
    public enum ReferenceType {
        MEETING,    // 회의
        TODO        // Todo
    }

    /**
     * 알림 유형 Enum
     */
    public enum NotificationType {
        MEETING_INVITATION, // 회의 초대
        TODO_ASSIGNED,      // Todo 할당
        TODO_REMINDER,      // Todo 리마인더
        MEETING_REMINDER,   // 회의 리마인더
        MINUTES_UPDATED,    // 회의록 수정
        TODO_COMPLETED      // Todo 완료
    }

    /**
     * 알림 상태 Enum
     */
    public enum NotificationStatus {
        PENDING,        // 대기 중
        PROCESSING,     // 처리 중
        SENT,           // 발송 완료
        FAILED,         // 발송 실패
        PARTIAL         // 부분 성공
    }

    /**
     * 발송 채널 Enum
     */
    public enum NotificationChannel {
        EMAIL,      // 이메일
        SMS,        // SMS
        PUSH        // Push 알림
    }
}
