// Package task provides consolidated task domain logic.
//
// This package serves as the single entry point for all task-related business logic.
// It re-exports functions from sub-packages for convenient imports.
//
// Architecture:
//
//	predicates/     - Pure Go predicate functions (SINGLE SOURCE OF TRUTH)
//	scopes/         - GORM scope functions (SQL mirrors of predicates)
//	categorization/ - Chain of Responsibility for kanban categorization
//
// Usage:
//
//	import "github.com/treytartt/honeydue-api/internal/task"
//
//	// Use predicates for in-memory checks
//	if task.IsCompleted(myTask) { ... }
//
//	// Use scopes for database queries
//	db.Scopes(task.ScopeOverdue(now)).Find(&tasks)
//
//	// Use categorization for kanban column determination
//	column := task.CategorizeTask(myTask, 30)
//
// For more details, see docs/TASK_LOGIC_ARCHITECTURE.md
package task

import (
	"time"

	"github.com/treytartt/honeydue-api/internal/models"
	"github.com/treytartt/honeydue-api/internal/task/categorization"
	"github.com/treytartt/honeydue-api/internal/task/predicates"
	"github.com/treytartt/honeydue-api/internal/task/scopes"
	"gorm.io/gorm"
)

// =============================================================================
// RE-EXPORTED TYPES
// =============================================================================

// KanbanColumn represents the possible kanban column names
type KanbanColumn = categorization.KanbanColumn

// Column constants
const (
	ColumnOverdue    = categorization.ColumnOverdue
	ColumnDueSoon    = categorization.ColumnDueSoon
	ColumnUpcoming   = categorization.ColumnUpcoming
	ColumnInProgress = categorization.ColumnInProgress
	ColumnCompleted  = categorization.ColumnCompleted
	ColumnCancelled  = categorization.ColumnCancelled
)

// =============================================================================
// RE-EXPORTED PREDICATES
// These are the SINGLE SOURCE OF TRUTH for task logic
// =============================================================================

// IsCompleted returns true if a task is considered "completed" per kanban rules.
// A task is completed when NextDueDate is nil AND has at least one completion.
func IsCompleted(task *models.Task) bool {
	return predicates.IsCompleted(task)
}

// IsActive returns true if the task is not cancelled and not archived.
func IsActive(task *models.Task) bool {
	return predicates.IsActive(task)
}

// IsCancelled returns true if the task has been cancelled.
func IsCancelled(task *models.Task) bool {
	return predicates.IsCancelled(task)
}

// IsArchived returns true if the task has been archived.
func IsArchived(task *models.Task) bool {
	return predicates.IsArchived(task)
}

// IsInProgress returns true if the task has status "In Progress".
func IsInProgress(task *models.Task) bool {
	return predicates.IsInProgress(task)
}

// EffectiveDate returns the date used for scheduling calculations.
// Prefers NextDueDate, falls back to DueDate.
func EffectiveDate(task *models.Task) *time.Time {
	return predicates.EffectiveDate(task)
}

// IsOverdue returns true if the task's effective date is in the past.
func IsOverdue(task *models.Task, now time.Time) bool {
	return predicates.IsOverdue(task, now)
}

// IsDueSoon returns true if the task's effective date is within the threshold.
func IsDueSoon(task *models.Task, now time.Time, daysThreshold int) bool {
	return predicates.IsDueSoon(task, now, daysThreshold)
}

// IsUpcoming returns true if the task is due after the threshold or has no due date.
func IsUpcoming(task *models.Task, now time.Time, daysThreshold int) bool {
	return predicates.IsUpcoming(task, now, daysThreshold)
}

// HasCompletions returns true if the task has at least one completion record.
func HasCompletions(task *models.Task) bool {
	return predicates.HasCompletions(task)
}

// GetCompletionCount returns the number of completions for a task.
// Supports both preloaded Completions slice and computed CompletionCount field.
func GetCompletionCount(task *models.Task) int {
	return predicates.GetCompletionCount(task)
}

// IsRecurring returns true if the task has a recurring frequency.
func IsRecurring(task *models.Task) bool {
	return predicates.IsRecurring(task)
}

// IsOneTime returns true if the task is a one-time (non-recurring) task.
func IsOneTime(task *models.Task) bool {
	return predicates.IsOneTime(task)
}

// =============================================================================
// RE-EXPORTED SCOPES
// These are SQL mirrors of the predicates for database queries
// =============================================================================

// ScopeActive filters to tasks that are not cancelled and not archived.
func ScopeActive(db *gorm.DB) *gorm.DB {
	return scopes.ScopeActive(db)
}

// ScopeCancelled filters to cancelled tasks only.
func ScopeCancelled(db *gorm.DB) *gorm.DB {
	return scopes.ScopeCancelled(db)
}

// ScopeArchived filters to archived tasks only.
func ScopeArchived(db *gorm.DB) *gorm.DB {
	return scopes.ScopeArchived(db)
}

// ScopeCompleted filters to completed tasks.
func ScopeCompleted(db *gorm.DB) *gorm.DB {
	return scopes.ScopeCompleted(db)
}

// ScopeNotCompleted excludes completed tasks.
func ScopeNotCompleted(db *gorm.DB) *gorm.DB {
	return scopes.ScopeNotCompleted(db)
}

// ScopeInProgress filters to tasks with status "In Progress".
func ScopeInProgress(db *gorm.DB) *gorm.DB {
	return scopes.ScopeInProgress(db)
}

// ScopeNotInProgress excludes tasks with status "In Progress".
func ScopeNotInProgress(db *gorm.DB) *gorm.DB {
	return scopes.ScopeNotInProgress(db)
}

// ScopeOverdue returns a scope for overdue tasks.
func ScopeOverdue(now time.Time) func(db *gorm.DB) *gorm.DB {
	return scopes.ScopeOverdue(now)
}

// ScopeDueSoon returns a scope for tasks due within the threshold.
func ScopeDueSoon(now time.Time, daysThreshold int) func(db *gorm.DB) *gorm.DB {
	return scopes.ScopeDueSoon(now, daysThreshold)
}

// ScopeUpcoming returns a scope for tasks due after the threshold or with no due date.
func ScopeUpcoming(now time.Time, daysThreshold int) func(db *gorm.DB) *gorm.DB {
	return scopes.ScopeUpcoming(now, daysThreshold)
}

// ScopeDueInRange returns a scope for tasks with effective date in a range.
func ScopeDueInRange(start, end time.Time) func(db *gorm.DB) *gorm.DB {
	return scopes.ScopeDueInRange(start, end)
}

// ScopeHasDueDate filters to tasks that have an effective due date.
func ScopeHasDueDate(db *gorm.DB) *gorm.DB {
	return scopes.ScopeHasDueDate(db)
}

// ScopeNoDueDate filters to tasks that have no effective due date.
func ScopeNoDueDate(db *gorm.DB) *gorm.DB {
	return scopes.ScopeNoDueDate(db)
}

// ScopeForResidence filters tasks by a single residence ID.
func ScopeForResidence(residenceID uint) func(db *gorm.DB) *gorm.DB {
	return scopes.ScopeForResidence(residenceID)
}

// ScopeForResidences filters tasks by multiple residence IDs.
func ScopeForResidences(residenceIDs []uint) func(db *gorm.DB) *gorm.DB {
	return scopes.ScopeForResidences(residenceIDs)
}

// ScopeHasCompletions filters to tasks that have at least one completion.
func ScopeHasCompletions(db *gorm.DB) *gorm.DB {
	return scopes.ScopeHasCompletions(db)
}

// ScopeNoCompletions filters to tasks that have no completions.
func ScopeNoCompletions(db *gorm.DB) *gorm.DB {
	return scopes.ScopeNoCompletions(db)
}

// ScopeOrderByDueDate orders tasks by effective due date ascending, nulls last.
func ScopeOrderByDueDate(db *gorm.DB) *gorm.DB {
	return scopes.ScopeOrderByDueDate(db)
}

// ScopeOrderByPriority orders tasks by priority level descending (urgent first).
func ScopeOrderByPriority(db *gorm.DB) *gorm.DB {
	return scopes.ScopeOrderByPriority(db)
}

// ScopeOrderByCreatedAt orders tasks by creation date descending (newest first).
func ScopeOrderByCreatedAt(db *gorm.DB) *gorm.DB {
	return scopes.ScopeOrderByCreatedAt(db)
}

// ScopeKanbanOrder applies the standard kanban ordering.
func ScopeKanbanOrder(db *gorm.DB) *gorm.DB {
	return scopes.ScopeKanbanOrder(db)
}

// =============================================================================
// RE-EXPORTED CATEGORIZATION
// =============================================================================

// CategorizeTask determines which kanban column a task belongs to.
func CategorizeTask(task *models.Task, daysThreshold int) KanbanColumn {
	return categorization.CategorizeTask(task, daysThreshold)
}

// DetermineKanbanColumn is a convenience function that returns the column as a string.
func DetermineKanbanColumn(task *models.Task, daysThreshold int) string {
	return categorization.DetermineKanbanColumn(task, daysThreshold)
}

// CategorizeTasksIntoColumns categorizes multiple tasks into their respective columns.
func CategorizeTasksIntoColumns(tasks []models.Task, daysThreshold int) map[KanbanColumn][]models.Task {
	return categorization.CategorizeTasksIntoColumns(tasks, daysThreshold)
}

// NewChain creates a new categorization chain for custom usage.
func NewChain() *categorization.Chain {
	return categorization.NewChain()
}
