package com.unicorn.hgzero.notification.domain;

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

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

/**
 * 알림 수신자 Entity
 *
 * 알림별 수신자 정보와 발송 상태를 관리
 * 수신자별로 발송 성공/실패 상태를 추적
 *
 * @author 준호 (Backend Developer)
 * @version 1.0
 * @since 2025-10-23
 */
@Entity
@Table(name = "notification_recipients", indexes = {
    @Index(name = "idx_recipient_notification", columnList = "notification_id"),
    @Index(name = "idx_recipient_email", columnList = "recipient_email"),
    @Index(name = "idx_recipient_status", columnList = "status")
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Comment("알림 수신자 정보")
public class NotificationRecipient {

    /**
     * 수신자 고유 ID (UUID)
     */
    @Id
    @Column(name = "recipient_id", length = 36, nullable = false)
    @Comment("수신자 고유 ID")
    private String recipientId;

    /**
     * 알림 (외래키)
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "notification_id", nullable = false)
    @Comment("알림 ID")
    private Notification notification;

    /**
     * 수신자 사용자 ID
     */
    @Column(name = "recipient_user_id", length = 100, nullable = false)
    @Comment("수신자 사용자 ID")
    private String recipientUserId;

    /**
     * 수신자 이름
     */
    @Column(name = "recipient_name", length = 200, nullable = false)
    @Comment("수신자 이름")
    private String recipientName;

    /**
     * 수신자 이메일
     */
    @Column(name = "recipient_email", length = 320, nullable = false)
    @Comment("수신자 이메일")
    private String recipientEmail;

    /**
     * 발송 상태 (PENDING, SENT, FAILED, RETRY)
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "status", length = 20, nullable = false)
    @Comment("발송 상태")
    private RecipientStatus status;

    /**
     * 재시도 횟수
     */
    @Column(name = "retry_count", nullable = false)
    @Comment("재시도 횟수")
    @Builder.Default
    private Integer retryCount = 0;

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

    /**
     * 실패 사유
     */
    @Column(name = "error_message", length = 1000)
    @Comment("실패 사유")
    private String errorMessage;

    /**
     * 다음 재시도 일시
     */
    @Column(name = "next_retry_at")
    @Comment("다음 재시도 일시")
    private LocalDateTime nextRetryAt;

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

    /**
     * 수정 일시
     */
    @Column(name = "updated_at")
    @Comment("수정 일시")
    private LocalDateTime updatedAt;

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

    /**
     * 수정 전 업데이트
     *
     * - updatedAt: 현재 시각
     */
    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    /**
     * 발송 성공 처리
     */
    public void markAsSent() {
        this.status = RecipientStatus.SENT;
        this.sentAt = LocalDateTime.now();
        this.errorMessage = null;
        this.nextRetryAt = null;
    }

    /**
     * 발송 실패 처리
     *
     * @param errorMessage 실패 사유
     */
    public void markAsFailed(String errorMessage) {
        this.status = RecipientStatus.FAILED;
        this.errorMessage = errorMessage;
        this.incrementRetryCount();
    }

    /**
     * 재시도 상태로 변경
     *
     * @param nextRetryAt 다음 재시도 일시
     */
    public void markForRetry(LocalDateTime nextRetryAt) {
        this.status = RecipientStatus.RETRY;
        this.nextRetryAt = nextRetryAt;
        this.incrementRetryCount();
    }

    /**
     * 재시도 횟수 증가
     */
    private void incrementRetryCount() {
        this.retryCount++;
    }

    /**
     * 최대 재시도 횟수 초과 여부 확인
     *
     * @param maxRetries 최대 재시도 횟수
     * @return 초과 여부
     */
    public boolean exceedsMaxRetries(int maxRetries) {
        return this.retryCount >= maxRetries;
    }

    /**
     * 재시도 가능 여부 확인
     *
     * @return 재시도 가능 여부
     */
    public boolean canRetry() {
        return this.status == RecipientStatus.RETRY
            && this.nextRetryAt != null
            && LocalDateTime.now().isAfter(this.nextRetryAt);
    }

    /**
     * 수신자 발송 상태 Enum
     */
    public enum RecipientStatus {
        PENDING,    // 대기 중
        SENT,       // 발송 완료
        FAILED,     // 발송 실패
        RETRY       // 재시도 예정
    }
}
