package com.social.media.domain.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Entity
@Table(name = "media", schema = "core_business",
       uniqueConstraints = {
           @UniqueConstraint(name = "media_media_code_key", columnNames = {"media_code"}),
           @UniqueConstraint(name = "media_stored_filename_key", columnNames = {"stored_filename"})
       },
       indexes = {
           @Index(name = "idx_media_company_id", columnList = "company_id"),
           @Index(name = "idx_media_uploaded_by", columnList = "uploaded_by"),
           @Index(name = "idx_media_format", columnList = "format"),
           @Index(name = "idx_media_is_public", columnList = "is_public"),
           @Index(name = "idx_media_validation_status", columnList = "validation_status"),
           @Index(name = "idx_media_uploaded_at", columnList = "uploaded_at"),
           @Index(name = "idx_media_file_size", columnList = "file_size")
       })
public class Media {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    
    @Column(name = "media_code", nullable = false, length = 20)
    private String mediaCode;
    
    @NotNull(message = "Company ID é obrigatório")
    @Column(name = "company_id", nullable = false)
    private Long companyId;
    
    @NotNull(message = "Uploaded By é obrigatório")
    @Column(name = "uploaded_by", nullable = false)
    private Long uploadedBy;
    
    @NotBlank(message = "Nome do arquivo original é obrigatório")
    @Size(max = 255, message = "Nome do arquivo original deve ter no máximo 255 caracteres")
    @Column(name = "original_filename", nullable = false, length = 255)
    private String originalFilename;
    
    @NotBlank(message = "Nome do arquivo armazenado é obrigatório")
    @Size(max = 255, message = "Nome do arquivo armazenado deve ter no máximo 255 caracteres")
    @Column(name = "stored_filename", nullable = false, length = 255)
    private String storedFilename;
    
    @NotBlank(message = "Caminho do arquivo é obrigatório")
    @Size(max = 1000, message = "Caminho do arquivo deve ter no máximo 1000 caracteres")
    @Column(name = "file_path", nullable = false, length = 1000)
    private String filePath;
    
    @NotNull(message = "Tamanho do arquivo é obrigatório")
    @Min(value = 1, message = "Tamanho do arquivo deve ser maior que 0")
    @Column(name = "file_size", nullable = false)
    private Long fileSize;
    
    @NotBlank(message = "Tipo MIME é obrigatório")
    @Size(max = 100, message = "Tipo MIME deve ter no máximo 100 caracteres")
    @Column(name = "mime_type", nullable = false, length = 100)
    private String mimeType;
    
    @NotNull(message = "Formato do arquivo é obrigatório")
    @Enumerated(EnumType.STRING)
    @Column(name = "format", nullable = false)
    private MediaFormat format;
    
    @Min(value = 1, message = "Largura deve ser maior que 0")
    @Column(name = "width")
    private Integer width;
    
    @Min(value = 1, message = "Altura deve ser maior que 0")
    @Column(name = "height")
    private Integer height;
    
    @Min(value = 1, message = "Duração deve ser maior que 0")
    @Column(name = "duration_seconds")
    private Integer durationSeconds;
    
    @Column(name = "is_public", nullable = false)
    private Boolean isPublic = false;
    
    @Size(max = 500, message = "Descrição deve ter no máximo 500 caracteres")
    @Column(name = "description", length = 500)
    private String description;
    
    @Size(max = 255, message = "Texto alternativo deve ter no máximo 255 caracteres")
    @Column(name = "alt_text", length = 255)
    private String altText;
    
    @JdbcTypeCode(SqlTypes.JSON)
    @Column(name = "metadata", columnDefinition = "jsonb")
    private Map<String, Object> metadata;
    
    @Size(max = 128, message = "Checksum do arquivo deve ter no máximo 128 caracteres")
    @Column(name = "file_checksum", length = 128)
    private String fileChecksum;
    
    @Size(max = 50, message = "Provedor de armazenamento deve ter no máximo 50 caracteres")
    @Column(name = "storage_provider", length = 50)
    private String storageProvider = "LOCAL";
    
    @NotNull(message = "Status de validação é obrigatório")
    @Enumerated(EnumType.STRING)
    @Column(name = "validation_status", nullable = false)
    private MediaValidationStatus validationStatus = MediaValidationStatus.PENDING;
    
    @Size(max = 1000, message = "Notas de validação devem ter no máximo 1000 caracteres")
    @Column(name = "validation_notes", length = 1000)
    private String validationNotes;
    
    @Column(name = "deleted_at")
    private LocalDateTime deletedAt;
    
    @NotNull(message = "Data de upload é obrigatória")
    @Column(name = "uploaded_at", nullable = false)
    private LocalDateTime uploadedAt;
    
    @NotNull(message = "Data de atualização é obrigatória")
    @Column(name = "updated_at", nullable = false)
    private LocalDateTime updatedAt;
    
    // Enums
    public enum MediaFormat {
        IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_WEBP, IMAGE_SVG, IMAGE_BMP, IMAGE_TIFF,
        VIDEO_MP4, VIDEO_AVI, VIDEO_MOV, VIDEO_WMV, VIDEO_FLV, VIDEO_WEBM, VIDEO_MKV,
        AUDIO_MP3, AUDIO_WAV, AUDIO_AAC, AUDIO_OGG, AUDIO_FLAC, AUDIO_M4A,
        DOCUMENT_PDF, DOCUMENT_DOC, DOCUMENT_DOCX, DOCUMENT_XLS, DOCUMENT_XLSX, 
        DOCUMENT_PPT, DOCUMENT_PPTX, DOCUMENT_TXT, DOCUMENT_RTF,
        ARCHIVE_ZIP, ARCHIVE_RAR, ARCHIVE_7Z, ARCHIVE_TAR, ARCHIVE_GZ,
        OTHER
    }
    
    public enum MediaValidationStatus {
        PENDING, APPROVED, REJECTED, UNDER_REVIEW, FLAGGED
    }
    
    // Constructors
    public Media() {
    }
    
    public Media(Long companyId, Long uploadedBy, String originalFilename, String storedFilename, 
                 String filePath, Long fileSize, String mimeType, MediaFormat format) {
        this.companyId = companyId;
        this.uploadedBy = uploadedBy;
        this.originalFilename = originalFilename;
        this.storedFilename = storedFilename;
        this.filePath = filePath;
        this.fileSize = fileSize;
        this.mimeType = mimeType;
        this.format = format;
        this.uploadedAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    // Lifecycle callbacks
    @PrePersist
    protected void onCreate() {
        LocalDateTime now = LocalDateTime.now();
        if (uploadedAt == null) {
            uploadedAt = now;
        }
        updatedAt = now;
        
        // Set default values if not provided
        if (isPublic == null) {
            isPublic = false;
        }
        if (metadata == null) {
            metadata = new HashMap<>();
        }
        if (storageProvider == null) {
            storageProvider = "LOCAL";
        }
        if (validationStatus == null) {
            validationStatus = MediaValidationStatus.PENDING;
        }
    }
    
    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
    
    // Business methods
    public boolean isImage() {
        return format != null && format.name().startsWith("IMAGE_");
    }
    
    public boolean isVideo() {
        return format != null && format.name().startsWith("VIDEO_");
    }
    
    public boolean isAudio() {
        return format != null && format.name().startsWith("AUDIO_");
    }
    
    public boolean isDocument() {
        return format != null && format.name().startsWith("DOCUMENT_");
    }
    
    public boolean isArchive() {
        return format != null && format.name().startsWith("ARCHIVE_");
    }
    
    public boolean isDeleted() {
        return deletedAt != null;
    }
    
    public boolean isApproved() {
        return validationStatus == MediaValidationStatus.APPROVED;
    }
    
    public boolean isPending() {
        return validationStatus == MediaValidationStatus.PENDING;
    }
    
    public boolean isRejected() {
        return validationStatus == MediaValidationStatus.REJECTED;
    }
    
    public String getFileExtension() {
        if (originalFilename != null && originalFilename.contains(".")) {
            return originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
        }
        return "";
    }
    
    public String getFileSizeFormatted() {
        if (fileSize == null) return "0 B";
        
        long bytes = fileSize;
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
        return String.format("%.1f GB", bytes / (1024.0 * 1024.0 * 1024.0));
    }
    
    public String getDurationFormatted() {
        if (durationSeconds == null) return null;
        
        int hours = durationSeconds / 3600;
        int minutes = (durationSeconds % 3600) / 60;
        int seconds = durationSeconds % 60;
        
        if (hours > 0) {
            return String.format("%d:%02d:%02d", hours, minutes, seconds);
        } else {
            return String.format("%d:%02d", minutes, seconds);
        }
    }
    
    public void markAsDeleted() {
        this.deletedAt = LocalDateTime.now();
    }
    
    public void restore() {
        this.deletedAt = null;
    }
    
    public void approve(String notes) {
        this.validationStatus = MediaValidationStatus.APPROVED;
        this.validationNotes = notes;
    }
    
    public void reject(String notes) {
        this.validationStatus = MediaValidationStatus.REJECTED;
        this.validationNotes = notes;
    }
    
    public void flag(String notes) {
        this.validationStatus = MediaValidationStatus.FLAGGED;
        this.validationNotes = notes;
    }
    
    // Getters and Setters
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getMediaCode() {
        return mediaCode;
    }
    
    public void setMediaCode(String mediaCode) {
        this.mediaCode = mediaCode;
    }
    
    public Long getCompanyId() {
        return companyId;
    }
    
    public void setCompanyId(Long companyId) {
        this.companyId = companyId;
    }
    
    public Long getUploadedBy() {
        return uploadedBy;
    }
    
    public void setUploadedBy(Long uploadedBy) {
        this.uploadedBy = uploadedBy;
    }
    
    public String getOriginalFilename() {
        return originalFilename;
    }
    
    public void setOriginalFilename(String originalFilename) {
        this.originalFilename = originalFilename;
    }
    
    public String getStoredFilename() {
        return storedFilename;
    }
    
    public void setStoredFilename(String storedFilename) {
        this.storedFilename = storedFilename;
    }
    
    public String getFilePath() {
        return filePath;
    }
    
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    
    public Long getFileSize() {
        return fileSize;
    }
    
    public void setFileSize(Long fileSize) {
        this.fileSize = fileSize;
    }
    
    public String getMimeType() {
        return mimeType;
    }
    
    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }
    
    public MediaFormat getFormat() {
        return format;
    }
    
    public void setFormat(MediaFormat format) {
        this.format = format;
    }
    
    public Integer getWidth() {
        return width;
    }
    
    public void setWidth(Integer width) {
        this.width = width;
    }
    
    public Integer getHeight() {
        return height;
    }
    
    public void setHeight(Integer height) {
        this.height = height;
    }
    
    public Integer getDurationSeconds() {
        return durationSeconds;
    }
    
    public void setDurationSeconds(Integer durationSeconds) {
        this.durationSeconds = durationSeconds;
    }
    
    public Boolean getIsPublic() {
        return isPublic;
    }
    
    public void setIsPublic(Boolean isPublic) {
        this.isPublic = isPublic;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public String getAltText() {
        return altText;
    }
    
    public void setAltText(String altText) {
        this.altText = altText;
    }
    
    public Map<String, Object> getMetadata() {
        return metadata;
    }
    
    public void setMetadata(Map<String, Object> metadata) {
        this.metadata = metadata;
    }
    
    public String getFileChecksum() {
        return fileChecksum;
    }
    
    public void setFileChecksum(String fileChecksum) {
        this.fileChecksum = fileChecksum;
    }
    
    public String getStorageProvider() {
        return storageProvider;
    }
    
    public void setStorageProvider(String storageProvider) {
        this.storageProvider = storageProvider;
    }
    
    public MediaValidationStatus getValidationStatus() {
        return validationStatus;
    }
    
    public void setValidationStatus(MediaValidationStatus validationStatus) {
        this.validationStatus = validationStatus;
    }
    
    public String getValidationNotes() {
        return validationNotes;
    }
    
    public void setValidationNotes(String validationNotes) {
        this.validationNotes = validationNotes;
    }
    
    public LocalDateTime getDeletedAt() {
        return deletedAt;
    }
    
    public void setDeletedAt(LocalDateTime deletedAt) {
        this.deletedAt = deletedAt;
    }
    
    public LocalDateTime getUploadedAt() {
        return uploadedAt;
    }
    
    public void setUploadedAt(LocalDateTime uploadedAt) {
        this.uploadedAt = uploadedAt;
    }
    
    public LocalDateTime getUpdatedAt() {
        return updatedAt;
    }
    
    public void setUpdatedAt(LocalDateTime updatedAt) {
        this.updatedAt = updatedAt;
    }
    
    // equals and hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Media media = (Media) o;
        return Objects.equals(id, media.id) && Objects.equals(storedFilename, media.storedFilename);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id, storedFilename);
    }
    
    @Override
    public String toString() {
        return "Media{" +
                "id=" + id +
                ", mediaCode='" + mediaCode + '\'' +
                ", companyId=" + companyId +
                ", uploadedBy=" + uploadedBy +
                ", originalFilename='" + originalFilename + '\'' +
                ", storedFilename='" + storedFilename + '\'' +
                ", fileSize=" + fileSize +
                ", format=" + format +
                ", validationStatus=" + validationStatus +
                ", isPublic=" + isPublic +
                ", uploadedAt=" + uploadedAt +
                '}';
    }
}
