package de.assecutor.votianlt.model;

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

import java.time.LocalDateTime;

/**
 * Represents a message that can be sent between the server and clients.
 * Messages can be either job-related or general messages.
 */
@Data
@NoArgsConstructor
@Document(collection = "messages")
public class Message {

    @Id
    @JsonIgnore
    private ObjectId id;

    /**
     * Content of the message, either plain text or base64 encoded media
     */
    @Field("content")
    private String content;

    /**
     * Declares how to interpret the content payload
     */
    @Field("content_type")
    private MessageContentType contentType = MessageContentType.TEXT;

    /**
     * AppUser ID (clientId) - the AppUser to whom this message belongs
     */
    @Field("receiver")
    private String receiver;

    /**
     * Optional stable client-side ID used for idempotent retries from the mobile
     * app.
     */
    @Field("client_message_id")
    private String clientMessageId;

    /**
     * Timestamp when the message was created
     */
    @Field("created_at")
    private LocalDateTime createdAt;

    /**
     * Origin of the message: INCOMING (from client), OUTGOING (to client), or
     * SERVER (from server)
     */
    @Field("origin")
    private MessageOrigin origin;

    /**
     * Type of message: JOB_RELATED or GENERAL
     */
    @Field("message_type")
    private MessageType messageType;

    /**
     * Optional reference to a job (only for job-related messages)
     */
    @Field("job_id")
    private ObjectId jobId;

    /**
     * Optional job number for easier reference (denormalized)
     */
    @Field("job_number")
    private String jobNumber;

    /**
     * Whether the message has been read by the receiver
     */
    @Field("is_read")
    private boolean isRead;

    /**
     * Timestamp when the message was read
     */
    @Field("read_at")
    private LocalDateTime readAt;

    /**
     * Delivery status: NOTSEND (failed to deliver), SEND (successfully delivered)
     */
    @Field("delivery_status")
    private MessageDeliveryStatus deliveryStatus;

    /**
     * Constructor for general messages
     */
    public Message(String content, String receiver, MessageOrigin origin) {
        this(content, receiver, origin, MessageContentType.TEXT);
    }

    /**
     * Constructor for general messages with explicit content type
     */
    public Message(String content, String receiver, MessageOrigin origin, MessageContentType contentType) {
        initializeBaseFields(content, receiver, origin, contentType);
        this.messageType = MessageType.GENERAL;
    }

    /**
     * Constructor for job-related messages
     */
    public Message(String content, String receiver, MessageOrigin origin, ObjectId jobId, String jobNumber) {
        this(content, receiver, origin, MessageContentType.TEXT, jobId, jobNumber);
    }

    /**
     * Constructor for job-related messages with explicit content type
     */
    public Message(String content, String receiver, MessageOrigin origin, MessageContentType contentType,
            ObjectId jobId, String jobNumber) {
        initializeBaseFields(content, receiver, origin, contentType);
        this.messageType = MessageType.JOB_RELATED;
        this.jobId = jobId;
        this.jobNumber = jobNumber;
    }

    /**
     * Mark the message as read
     */
    public void markAsRead() {
        this.isRead = true;
        this.readAt = LocalDateTime.now();
    }

    /**
     * Mark the message as sent (successfully delivered)
     */
    public void markAsSent() {
        this.deliveryStatus = MessageDeliveryStatus.SEND;
    }

    /**
     * Mark the message as not sent (delivery failed)
     */
    public void markAsNotSent() {
        this.deliveryStatus = MessageDeliveryStatus.NOTSEND;
    }

    /**
     * Check if message was successfully delivered
     */
    public boolean isDelivered() {
        return deliveryStatus == MessageDeliveryStatus.SEND;
    }

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

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

    /**
     * Ensure callers always receive a non-null content type to simplify rendering
     */
    public MessageContentType getContentType() {
        return contentType != null ? contentType : MessageContentType.TEXT;
    }

    private void initializeBaseFields(String content, String receiver, MessageOrigin origin,
            MessageContentType contentType) {
        this.content = content;
        this.receiver = receiver;
        this.origin = origin;
        this.createdAt = LocalDateTime.now();
        this.isRead = false;
        this.contentType = contentType != null ? contentType : MessageContentType.TEXT;
        // Server messages start as NOTSEND until confirmed delivered
        this.deliveryStatus = (origin == MessageOrigin.SERVER) ? MessageDeliveryStatus.NOTSEND : null;
    }
}
