// Package semantic defines durable task types for async semantic processing.
package semantic

import (
	"errors"
	"time"
)

// ImgExtractTaskPayload carries non-authoritative hints for durable image text
// extraction. The task identity remains resource_id + resource_version.
type ImgExtractTaskPayload struct {
	Path        string `json:"path,omitempty"`
	ContentType string `json:"content_type,omitempty"`
}

// EmbedTaskPayload pins work to the effective provider configuration that
// admitted it. Tenant-managed revisions are monotonic, so stale backends
// cannot replace work admitted by a newer tenant configuration.
type EmbedTaskPayload struct {
	Generation     uint64 `json:"generation"`
	ConfigRevision uint64 `json:"config_revision"`
	TenantManaged  bool   `json:"tenant_managed,omitempty"`
}

// EmbedConfigOrder describes an incoming embedding configuration relative to
// the configuration already pinned to a durable task.
type EmbedConfigOrder uint8

const (
	EmbedConfigSame EmbedConfigOrder = iota
	EmbedConfigNewer
	EmbedConfigStale
)

// CompareEmbedConfig orders tenant-managed revisions monotonically and keeps
// tenant-managed work authoritative over a process default. Process-default
// revisions are stable fingerprints rather than counters, so a different
// admitted default replaces the existing default during a rolling deployment.
func CompareEmbedConfig(incoming, existing EmbedTaskPayload) EmbedConfigOrder {
	incomingValid := incoming.Generation > 0 && incoming.ConfigRevision > 0
	existingValid := existing.Generation > 0 && existing.ConfigRevision > 0
	if !incomingValid {
		if existingValid {
			return EmbedConfigStale
		}
		if incoming.Generation > 0 && incoming.Generation != existing.Generation {
			return EmbedConfigNewer
		}
		return EmbedConfigSame
	}
	if !existingValid {
		return EmbedConfigNewer
	}
	if incoming == existing {
		return EmbedConfigSame
	}
	if incoming.TenantManaged != existing.TenantManaged {
		if incoming.TenantManaged {
			return EmbedConfigNewer
		}
		return EmbedConfigStale
	}
	if incoming.TenantManaged {
		if incoming.ConfigRevision > existing.ConfigRevision {
			return EmbedConfigNewer
		}
		return EmbedConfigStale
	}
	return EmbedConfigNewer
}

// EmbedBackfillTaskPayload checkpoints one tenant-scoped keyset scan.
type EmbedBackfillTaskPayload struct {
	Generation     uint64 `json:"generation"`
	ConfigRevision uint64 `json:"config_revision"`
	AfterInodeID   string `json:"after_inode_id,omitempty"`
}

// AudioExtractTaskPayload carries non-authoritative hints for durable audio
// transcript extraction. The task identity remains resource_id + resource_version.
type AudioExtractTaskPayload struct {
	Path              string            `json:"path,omitempty"`
	ContentType       string            `json:"content_type,omitempty"`
	VideoFallback     bool              `json:"video_fallback,omitempty"`
	CompletedSegments int               `json:"completed_segments,omitempty"`
	Text              string            `json:"text,omitempty"`
	Tags              map[string]string `json:"tags,omitempty"`
	Structured        bool              `json:"structured,omitempty"`
}

// VideoExtractTaskPayload carries non-authoritative hints for durable video
// visual extraction. The task identity remains resource_id + resource_version.
type VideoExtractTaskPayload struct {
	Path                    string            `json:"path,omitempty"`
	ContentType             string            `json:"content_type,omitempty"`
	VisualDone              bool              `json:"visual_done,omitempty"`
	VisualText              string            `json:"visual_text,omitempty"`
	VisualCompletedSegments int               `json:"visual_completed_segments,omitempty"`
	VisualSegmentCount      int               `json:"visual_segment_count,omitempty"`
	VisualSegmentsText      string            `json:"visual_segments_text,omitempty"`
	AudioDone               bool              `json:"audio_done,omitempty"`
	AudioPresent            bool              `json:"audio_present,omitempty"`
	AudioCompletedSegments  int               `json:"audio_completed_segments,omitempty"`
	AudioText               string            `json:"audio_text,omitempty"`
	VisualTags              map[string]string `json:"visual_tags,omitempty"`
	AudioTags               map[string]string `json:"audio_tags,omitempty"`
}

// TaskType identifies the durable semantic work to execute.
type TaskType string

const (
	// TaskTypeEmbed generates or refreshes file embeddings.
	TaskTypeEmbed TaskType = "embed"
	// TaskTypeEmbedBackfill schedules generation-aware embed work for existing files.
	TaskTypeEmbedBackfill TaskType = "embed_backfill"
	// TaskTypeImgExtractText extracts or refreshes image-derived file content_text.
	TaskTypeImgExtractText TaskType = "img_extract_text"
	// TaskTypeAudioExtractText extracts or refreshes audio-derived file content_text.
	TaskTypeAudioExtractText TaskType = "audio_extract_text"
	// TaskTypeVideoExtractVisual extracts or refreshes video-derived visual content_text.
	TaskTypeVideoExtractVisual TaskType = "video_extract_visual"
	// TaskTypeGenerateL0 generates or refreshes .abstract.md style summaries.
	TaskTypeGenerateL0 TaskType = "generate_l0"
	// TaskTypeGenerateL1 generates or refreshes .overview.md style summaries.
	TaskTypeGenerateL1 TaskType = "generate_l1"
)

// TaskStatus describes the delivery state of a semantic task.
type TaskStatus string

const (
	TaskQueued       TaskStatus = "queued"
	TaskProcessing   TaskStatus = "processing"
	TaskSucceeded    TaskStatus = "succeeded"
	TaskFailed       TaskStatus = "failed"
	TaskDeadLettered TaskStatus = "dead_lettered"
)

var (
	// ErrTaskNotFound reports that the requested task row does not exist.
	ErrTaskNotFound = errors.New("semantic task not found")
	// ErrTaskLeaseMismatch reports that the supplied receipt is stale, wrong, or expired.
	ErrTaskLeaseMismatch = errors.New("semantic task lease mismatch")
)

// Task mirrors one row in the tenant-local semantic_tasks table defined in
// pkg/tenant/schema_zero.go and pkg/tenant/schema_db9.go. Keep this struct in
// sync with the table shape and lifecycle semantics documented in
// docs/async-embedding/async-embedding-generation-proposal.md.
type Task struct {
	TaskID          string
	TaskType        TaskType
	ResourceID      string
	ResourceVersion int64
	Status          TaskStatus
	AttemptCount    int
	MaxAttempts     int
	Receipt         string
	LeasedAt        *time.Time
	LeaseUntil      *time.Time
	AvailableAt     time.Time
	PayloadJSON     []byte
	LastError       string
	CreatedAt       time.Time
	UpdatedAt       time.Time
	CompletedAt     *time.Time
}

// ClaimResult returns one claimed task when the queue has work available.
type ClaimResult struct {
	Task  Task
	Found bool
}
