package main

import (
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// Mark mirrors the per-photo extras the web client stores via the marks
// endpoints — rating + four-colour label. PhotoUID is the row key; both
// payload fields are nullable so the sparse "no rating / no colour" state
// round-trips cleanly. The Node prototype kept this in a JSON file; we
// migrate to MariaDB here so the M4 sharing work has a real table to
// extend.
type Mark struct {
	PhotoUID  string    `gorm:"primaryKey;size:64;column:photo_uid" json:"-"`
	Rating    *int      `gorm:"column:rating" json:"rating,omitempty"`
	Color     *string   `gorm:"size:16;column:color" json:"color,omitempty"`
	UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"`
}

// TableName pins the GORM-pluralised default to a name that matches the
// other tables the M4 plan calls out (`marks`, `heap_shares`, …) so
// nothing surprising lands in the schema.
func (Mark) TableName() string { return "marks" }

// asJSON returns the wire shape clients expect — same flat object the
// Node prototype emitted. An empty Mark (rating=nil, color=nil) renders
// as `{}` which the client treats as "no mark on this photo".
func (m *Mark) asJSON() map[string]any {
	out := map[string]any{}
	if m == nil {
		return out
	}
	if m.Rating != nil {
		out["rating"] = *m.Rating
	}
	if m.Color != nil && *m.Color != "" {
		out["color"] = *m.Color
	}
	if !m.UpdatedAt.IsZero() {
		// ISO-8601 with millisecond precision, UTC — matches the Node
		// prototype's `new Date().toISOString()` so clients written against
		// the old endpoint stay happy.
		out["updatedAt"] = m.UpdatedAt.UTC().Format("2006-01-02T15:04:05.000Z")
	}
	return out
}

func openDB(dsn string) (*gorm.DB, error) {
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Warn),
	})
	if err != nil {
		return nil, err
	}
	if err := db.AutoMigrate(&Mark{}); err != nil {
		return nil, err
	}
	return db, nil
}
