package filemanager

import (
	"strings"
	"time"

	"gorm.io/gorm"

	"wucher/internal/shared/pkg/uuidv7"
)

const (
	FileStatusUploaded   = "uploaded"
	FileStatusProcessing = "processing"
	FileStatusValidated  = "validated"
	FileStatusFailed     = "failed"
	FileStatusReady      = "ready"
	FileStatusTrashed    = "trashed"

	FileLifecycleStatusTemp          = "temp"
	FileLifecycleStatusDraft         = "draft"
	FileLifecycleStatusActive        = "active"
	FileLifecycleStatusFinal         = "final"
	FileLifecycleStatusOrphanPending = "orphan_pending"
	FileLifecycleStatusDeleted       = "deleted"
)

type File struct {
	ID                     []byte     `gorm:"type:binary(16);primaryKey;column:id"`
	FolderID               []byte     `gorm:"type:binary(16);not null;column:folder_id;index:idx_file_files_folder_active,priority:1;index:uq_file_files_folder_name_slot,priority:1"`
	Name                   string     `gorm:"type:varchar(255);not null;column:name"`
	NameNormalized         string     `gorm:"type:varchar(255);not null;column:name_normalized;index:uq_file_files_folder_name_slot,priority:2"`
	Extension              string     `gorm:"type:varchar(32);column:extension"`
	SizeBytes              int64      `gorm:"type:bigint unsigned;not null;default:0;column:size_bytes"`
	MimeType               string     `gorm:"type:varchar(255);not null;column:mime_type"`
	Bucket                 string     `gorm:"type:varchar(128);not null;column:bucket"`
	ObjectKey              string     `gorm:"type:varchar(512);not null;column:object_key;uniqueIndex:uq_file_files_object_key"`
	ObjectVersion          string     `gorm:"type:varchar(128);column:object_version"`
	ETag                   string     `gorm:"type:varchar(128);column:etag"`
	ThumbnailMimeType      *string    `gorm:"type:varchar(255);column:thumbnail_mime_type"`
	ThumbnailSizeBytes     int64      `gorm:"type:bigint unsigned;column:thumbnail_size_bytes"`
	ThumbnailBucket        *string    `gorm:"type:varchar(128);column:thumbnail_bucket"`
	ThumbnailObjectKey     *string    `gorm:"type:varchar(512);column:thumbnail_object_key;uniqueIndex:uq_file_files_thumbnail_object_key"`
	ThumbnailObjectVersion *string    `gorm:"type:varchar(128);column:thumbnail_object_version"`
	ThumbnailETag          *string    `gorm:"type:varchar(128);column:thumbnail_etag"`
	PreviewMimeType        *string    `gorm:"type:varchar(255);column:preview_mime_type"`
	PreviewSizeBytes       int64      `gorm:"type:bigint unsigned;column:preview_size_bytes"`
	PreviewBucket          *string    `gorm:"type:varchar(128);column:preview_bucket"`
	PreviewObjectKey       *string    `gorm:"type:varchar(512);column:preview_object_key;uniqueIndex:uq_file_files_preview_object_key"`
	PreviewObjectVersion   *string    `gorm:"type:varchar(128);column:preview_object_version"`
	PreviewETag            *string    `gorm:"type:varchar(128);column:preview_etag"`
	ChecksumSHA256         string     `gorm:"type:char(64);column:checksum_sha256"`
	IsTemplate             bool       `gorm:"column:is_template;not null;default:false"`
	TemplateUUID           []byte     `gorm:"type:binary(16);column:template_uuid;index:idx_file_files_template_takeover,priority:1"`
	TakeoverID             []byte     `gorm:"type:binary(16);column:takeover_id;index:idx_file_files_template_takeover,priority:2"`
	Status                 string     `gorm:"type:varchar(32);not null;default:'uploaded';column:status;index:idx_file_files_status_updated,priority:1"`
	ProcessingStartedAt    *time.Time `gorm:"column:processing_started_at"`
	ProcessedAt            *time.Time `gorm:"column:processed_at"`
	ValidatedAt            *time.Time `gorm:"column:validated_at"`
	FailedAt               *time.Time `gorm:"column:failed_at"`
	FailureReason          string     `gorm:"type:text;column:failure_reason"`
	UploadError            string     `gorm:"type:text;column:upload_error"`
	NameSlot               string     `gorm:"type:char(36);not null;default:'live';column:name_slot;index:uq_file_files_folder_name_slot,priority:3"`
	CreatedAt              time.Time  `gorm:"column:created_at"`
	CreatedBy              []byte     `gorm:"type:binary(16);column:created_by"`
	UpdatedAt              time.Time  `gorm:"column:updated_at;index:idx_file_files_status_updated,priority:2"`
	UpdatedBy              []byte     `gorm:"type:binary(16);column:updated_by"`
	DeletedAt              *time.Time `gorm:"column:deleted_at;index:idx_file_files_folder_active,priority:2"`
	DeletedBy              []byte     `gorm:"type:binary(16);column:deleted_by"`
	TrashedAt              *time.Time `gorm:"column:trashed_at"`
	PurgeAt                *time.Time `gorm:"column:purge_at;index:idx_file_files_purge_at"`
	LifecycleStatus        string     `gorm:"type:varchar(32);not null;default:'temp';column:lifecycle_status;index:idx_file_files_lifecycle_status_orphaned,priority:1"`
	AttachedAt             *time.Time `gorm:"column:attached_at"`
	OrphanedAt             *time.Time `gorm:"column:orphaned_at;index:idx_file_files_lifecycle_status_orphaned,priority:2"`
	LifecycleDeletedAt     *time.Time `gorm:"column:lifecycle_deleted_at"`
	WOPILockID             string     `gorm:"type:varchar(128);column:wopi_lock_id"`
	WOPILockJTI            string     `gorm:"type:varchar(128);column:wopi_lock_jti"`
	WOPILockedAt           *time.Time `gorm:"column:wopi_locked_at"`
	WOPILockExpiresAt      *time.Time `gorm:"column:wopi_lock_expires_at"`

	Folder *Folder `gorm:"foreignKey:FolderID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT"`
}

func (File) TableName() string { return "file_files" }

func (f *File) BeforeCreate(tx *gorm.DB) error {
	if len(f.ID) == 0 {
		f.ID = uuidv7.MustBytes()
	}
	return nil
}

func NormalizeFileStatus(status string) string {
	s := strings.ToLower(strings.TrimSpace(status))
	switch s {
	case "pending":
		return FileStatusUploaded
	case "uploading":
		return FileStatusProcessing
	default:
		return s
	}
}

func IsKnownFileStatus(status string) bool {
	switch NormalizeFileStatus(status) {
	case FileStatusUploaded, FileStatusProcessing, FileStatusValidated, FileStatusFailed, FileStatusReady, FileStatusTrashed:
		return true
	default:
		return false
	}
}

func CanTransitionFileStatus(fromStatus, toStatus string) bool {
	from := NormalizeFileStatus(fromStatus)
	to := NormalizeFileStatus(toStatus)

	switch from {
	case FileStatusUploaded:
		return to == FileStatusProcessing || to == FileStatusFailed
	case FileStatusProcessing:
		return to == FileStatusValidated || to == FileStatusFailed
	case FileStatusValidated:
		return to == FileStatusReady || to == FileStatusFailed
	case FileStatusReady:
		return to == FileStatusTrashed
	default:
		return false
	}
}

func NormalizeFileLifecycleStatus(status string) string {
	s := strings.ToLower(strings.TrimSpace(status))
	switch s {
	case "":
		return FileLifecycleStatusTemp
	default:
		return s
	}
}

func IsKnownFileLifecycleStatus(status string) bool {
	switch NormalizeFileLifecycleStatus(status) {
	case FileLifecycleStatusTemp, FileLifecycleStatusDraft, FileLifecycleStatusActive, FileLifecycleStatusFinal, FileLifecycleStatusOrphanPending, FileLifecycleStatusDeleted:
		return true
	default:
		return false
	}
}
