package de.assecutor.votianlt.messaging.model;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.time.LocalDateTime;

/**
 * Represents a message delivery that is pending acknowledgment.
 * Stored in MongoDB for retry and tracking purposes.
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "pending_deliveries")
public class PendingDelivery {

    @Id
    @JsonIgnore
    private ObjectId id;

    /**
     * Unique message identifier
     */
    @Field("message_id")
    @Indexed(unique = true)
    private String messageId;

    /**
     * Target topic for this message
     */
    @Field("topic")
    private String topic;

    /**
     * Serialized envelope data (JSON bytes)
     */
    @Field("envelope_data")
    private byte[] envelopeData;

    /**
     * Current delivery status
     */
    @Field("status")
    @Indexed
    private DeliveryStatus status;

    /**
     * When the delivery record was created
     */
    @Field("created_at")
    private LocalDateTime createdAt;

    /**
     * When the message was last sent
     */
    @Field("sent_at")
    private LocalDateTime sentAt;

    /**
     * When acknowledgment was received
     */
    @Field("acknowledged_at")
    private LocalDateTime acknowledgedAt;

    /**
     * Number of retry attempts made
     */
    @Field("retry_count")
    private int retryCount;

    /**
     * Maximum number of retries allowed
     */
    @Field("max_retries")
    private int maxRetries;

    /**
     * When the next retry should be attempted
     */
    @Field("next_retry_at")
    @Indexed
    private LocalDateTime nextRetryAt;

    /**
     * When this delivery expires
     */
    @Field("expires_at")
    @Indexed
    private LocalDateTime expiresAt;

    /**
     * Reason for failure (if status is FAILED)
     */
    @Field("failure_reason")
    private String failureReason;

    /**
     * Client ID (extracted from topic if available)
     */
    @Field("client_id")
    private String clientId;

    /**
     * Constructor for new pending delivery
     */
    public PendingDelivery(String messageId, String topic, byte[] envelopeData, 
                          int maxRetries, LocalDateTime expiresAt) {
        this.messageId = messageId;
        this.topic = topic;
        this.envelopeData = envelopeData;
        this.status = DeliveryStatus.PENDING;
        this.createdAt = LocalDateTime.now();
        this.retryCount = 0;
        this.maxRetries = maxRetries;
        this.expiresAt = expiresAt;
        this.clientId = extractClientIdFromTopic(topic);
    }

    /**
     * Mark as sent and schedule next retry
     */
    public void markAsSent(LocalDateTime nextRetryAt) {
        this.status = DeliveryStatus.SENT;
        this.sentAt = LocalDateTime.now();
        this.nextRetryAt = nextRetryAt;
    }

    /**
     * Mark as acknowledged
     */
    public void markAsAcknowledged() {
        this.status = DeliveryStatus.ACKNOWLEDGED;
        this.acknowledgedAt = LocalDateTime.now();
    }

    /**
     * Mark as failed with reason
     */
    public void markAsFailed(String reason) {
        this.status = DeliveryStatus.FAILED;
        this.failureReason = reason;
    }

    /**
     * Mark as expired
     */
    public void markAsExpired() {
        this.status = DeliveryStatus.EXPIRED;
        this.failureReason = "Message expired before delivery could be confirmed";
    }

    /**
     * Increment retry count
     */
    public void incrementRetryCount() {
        this.retryCount++;
    }

    /**
     * Check if max retries reached
     */
    public boolean hasReachedMaxRetries() {
        return retryCount >= maxRetries;
    }

    /**
     * Check if expired
     */
    public boolean isExpired() {
        return expiresAt != null && LocalDateTime.now().isAfter(expiresAt);
    }

    /**
     * Check if ready for retry
     */
    public boolean isReadyForRetry() {
        return status == DeliveryStatus.SENT 
            && nextRetryAt != null 
            && LocalDateTime.now().isAfter(nextRetryAt)
            && !hasReachedMaxRetries()
            && !isExpired();
    }

    /**
     * Extract client ID from topic pattern /client/{clientId}/...
     */
    private String extractClientIdFromTopic(String topic) {
        if (topic != null && topic.startsWith("/client/")) {
            String[] parts = topic.split("/");
            if (parts.length > 2) {
                return parts[2];
            }
        }
        return null;
    }

    /**
     * Returns the ObjectId as string for JSON serialization
     */
    @JsonGetter("id")
    public String getIdAsString() {
        return id != null ? id.toString() : null;
    }
}

