// Package album provides types and job handlers for anchor-based image album generation.
//
// An album is a photography session: one subject, one anchor image, and N shots.
// The anchor image establishes visual identity (same face/product/character).
// Each shot has its own direction (pose, lighting, setting) but references the anchor.
//
// Usage (standalone mode):
//
//	albumRepo := memory.NewAlbumRepository()
//	memQueue.RegisterHandler("generate_anchor", album.AnchorHandler(mg, store, pub, albumRepo, logger))
//	memQueue.RegisterHandler("generate_shot", album.ShotHandler(mg, store, pub, albumRepo, logger))
package album

import (
	"context"
	"errors"
	"time"
)

// ErrAnchorRequired is returned when shot generation is attempted before the anchor image exists.
// Handlers should map this to HTTP 422 Unprocessable Entity.
var ErrAnchorRequired = errors.New("anchor must be generated before shots")

// AlbumID is the unique identifier for an album.
type AlbumID string

// Album represents a photography session: one subject, one anchor, N shots.
type Album struct {
	ID          AlbumID   `json:"id"`
	UserID      string    `json:"userId"`
	Name        string    `json:"name"`        // Human label: "Jordan Headshots", "Product Launch Set"
	SubjectDesc string    `json:"subjectDesc"` // Natural language: what/who is being photographed
	AnchorURL   string    `json:"anchorUrl"`   // Stored anchor image — empty until generated
	AnchorJobID string    `json:"anchorJobId"` // Tracks the anchor generation job
	Shots       []Shot    `json:"shots"`
	CreatedAt   time.Time `json:"createdAt"`
	UpdatedAt   time.Time `json:"updatedAt"`
}

// Shot is one image in an album with its own generation direction.
type Shot struct {
	Index       int        `json:"index"`     // 0-indexed position
	Label       string     `json:"label"`     // Human label: "Headshot", "Outdoor Casual"
	Direction   string     `json:"direction"` // What makes this shot unique
	ImageURL    string     `json:"imageUrl"`  // Empty until generation completes
	JobID       string     `json:"jobId"`     // Tracks the async generation job
	Status      ShotStatus `json:"status"`
	Error       string     `json:"error,omitempty"` // Set when Status == ShotFailed
	GeneratedAt *time.Time `json:"generatedAt,omitempty"`
}

// ShotStatus represents the generation state of a shot.
type ShotStatus string

const (
	ShotPending    ShotStatus = "pending"
	ShotGenerating ShotStatus = "generating"
	ShotComplete   ShotStatus = "complete"
	ShotFailed     ShotStatus = "failed"
)

// ShotTemplate defines a reusable shot direction.
// Built-in templates (PortraitSession, ProductShoot, CharacterSheet) ship with the skeleton.
// Teams replace them with domain-specific templates for their use case.
type ShotTemplate struct {
	Label     string // Human-readable name shown in the UI
	Direction string // Added to the subject description to guide generation
}

// ---------------------------------------------------------------------------
// AlbumUpdater — minimal interface for job handlers
// ---------------------------------------------------------------------------

// AlbumUpdater is the minimal interface the job handlers need to persist generation results.
// The full AlbumRepository (in port package) extends this with CRUD operations.
type AlbumUpdater interface {
	// UpdateAnchor stores the generated anchor URL for an album.
	UpdateAnchor(ctx context.Context, albumID AlbumID, userID, anchorURL, anchorJobID string) error

	// UpdateShot stores the generated image URL and status for a specific shot.
	UpdateShot(ctx context.Context, albumID AlbumID, userID string, shotIndex int, imageURL string, status ShotStatus, shotError string) error
}

// ---------------------------------------------------------------------------
// Job payloads
// ---------------------------------------------------------------------------

// AnchorPayload is the job payload for "generate_anchor" jobs.
// The anchor is generated without a reference image — it IS the reference.
type AnchorPayload struct {
	AlbumID     string `json:"albumId"`
	UserID      string `json:"userId"`
	SubjectDesc string `json:"subjectDesc"` // Natural language description of the subject
}

// ShotPayload is the job payload for "generate_shot" jobs.
// The worker fetches the anchor bytes from AnchorURL at execution time.
// Bytes are NOT stored in the queue payload (too large, and URLs are stable).
type ShotPayload struct {
	AlbumID     string `json:"albumId"`
	UserID      string `json:"userId"`
	ShotIndex   int    `json:"shotIndex"`
	AnchorURL   string `json:"anchorUrl"`   // Worker fetches this at execution time
	SubjectDesc string `json:"subjectDesc"` // Combined with Direction for the final prompt
	Direction   string `json:"direction"`   // Shot-specific direction
}

// ---------------------------------------------------------------------------
// SSE event type constants for album events
// ---------------------------------------------------------------------------

// Album-specific SSE event types.
// All delivered on the existing user:<userId> channel.
const (
	EventAlbumAnchorComplete = "album_anchor_complete"
	EventAlbumAnchorFailed   = "album_anchor_failed"
	EventAlbumShotGenerating = "album_shot_generating"
	EventAlbumShotComplete   = "album_shot_complete"
	EventAlbumShotFailed     = "album_shot_failed"
)

// AlbumAnchorCompleteData is the SSE payload for album_anchor_complete events.
type AlbumAnchorCompleteData struct {
	AlbumID   string `json:"albumId"`
	AnchorURL string `json:"anchorUrl"`
}

// AlbumShotCompleteData is the SSE payload for album_shot_complete events.
type AlbumShotCompleteData struct {
	AlbumID   string `json:"albumId"`
	ShotIndex int    `json:"shotIndex"`
	ImageURL  string `json:"imageUrl"`
}

// AlbumShotFailedData is the SSE payload for album_shot_failed events.
type AlbumShotFailedData struct {
	AlbumID   string `json:"albumId"`
	ShotIndex int    `json:"shotIndex"`
	Error     string `json:"error"`
}

// AlbumAnchorFailedData is the SSE payload for album_anchor_failed events.
type AlbumAnchorFailedData struct {
	AlbumID string `json:"albumId"`
	Error   string `json:"error"`
}
