package filemanager

import (
	"context"
	"errors"
	"strings"
	"time"

	"gorm.io/gorm"

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

type FileStatusRealtimeEventInput struct {
	FileID        []byte
	UserID        []byte
	Status        string
	Name          string
	FailureReason string
	OccurredAt    time.Time
}

type FileStatusRealtimeEventWriter interface {
	CreateFileStatusRealtimeEvent(ctx context.Context, input FileStatusRealtimeEventInput) error
}

type FileStatusRealtimeEvent struct {
	ID            []byte     `gorm:"type:binary(16);primaryKey;column:id"`
	FileID        []byte     `gorm:"type:binary(16);index;not null;column:file_id"`
	UserID        []byte     `gorm:"type:binary(16);index;not null;column:user_id"`
	Status        string     `gorm:"type:varchar(32);index;not null;column:status"`
	Name          string     `gorm:"type:varchar(255);not null;column:name"`
	FailureReason string     `gorm:"type:text;column:failure_reason"`
	OccurredAt    time.Time  `gorm:"index;not null;column:occurred_at"`
	DeliveredAt   *time.Time `gorm:"index;column:delivered_at"`
	CreatedAt     time.Time  `gorm:"column:created_at"`
	UpdatedAt     time.Time  `gorm:"column:updated_at"`
}

func (FileStatusRealtimeEvent) TableName() string {
	return "file_status_realtime_events"
}

func (e *FileStatusRealtimeEvent) BeforeCreate(_ *gorm.DB) error {
	if len(e.ID) == 0 {
		e.ID = uuidv7.MustBytes()
	}
	if e.OccurredAt.IsZero() {
		e.OccurredAt = time.Now().UTC()
	}
	return nil
}

func NewFileStatusRealtimeEvent(input FileStatusRealtimeEventInput) (*FileStatusRealtimeEvent, error) {
	if len(input.FileID) != 16 {
		return nil, errors.New("file id must be uuid bytes")
	}
	if len(input.UserID) != 16 {
		return nil, errors.New("user id must be uuid bytes")
	}
	status := NormalizeFileStatus(input.Status)
	if !IsKnownFileStatus(status) {
		return nil, errors.New("invalid status")
	}
	occurredAt := input.OccurredAt.UTC()
	if input.OccurredAt.IsZero() {
		occurredAt = time.Now().UTC()
	}
	return &FileStatusRealtimeEvent{
		FileID:        append([]byte(nil), input.FileID...),
		UserID:        append([]byte(nil), input.UserID...),
		Status:        status,
		Name:          strings.TrimSpace(input.Name),
		FailureReason: strings.TrimSpace(input.FailureReason),
		OccurredAt:    occurredAt,
	}, nil
}
