// Package schedule owns AgentTeam-managed schedule definitions and automation run persistence.
package schedule

import (
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/runeffect"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

const (
	DefinitionStatusActive    = "active"
	DefinitionStatusPaused    = "paused"
	DefinitionStatusBlocked   = "blocked"
	DefinitionStatusCompleted = "completed"

	SourceKindTime = "time"

	SourceTypeOneShotAbsolute = "one_shot_absolute"
	SourceTypeOneShotRelative = "one_shot_relative"
	SourceTypeInterval        = "interval"
	SourceTypeCron            = "cron"

	TargetKindChannel     = "channel"
	TargetKindReplyThread = "reply_thread"
	TargetKindDM          = "dm"
	TargetKindTask        = "task"

	NotificationModeNone = "none"

	AutomationKindSchedule = "schedule"

	FailureCodeScheduleNotActive       = "schedule_not_active"
	FailureCodeInvalidSchedule         = "invalid_schedule_definition"
	FailureCodeTargetNotFound          = "target_not_found"
	FailureCodeForbiddenCurrentPerms   = "forbidden_current_permissions"
	FailureCodeContentInvalid          = "content_invalid"
	FailureCodeTransientWriteError     = "transient_write_error"
	FailureCodeOwnerMembershipLost     = "owner_membership_lost"
	FailureCodeSourceKindNotEnabled    = "source_kind_not_enabled"
	FailureCodePermissionScopeNotFound = "permission_scope_not_found"
)

type RunStatus = runeffect.Status

const (
	RunStatusRunning      = runeffect.StatusRunning
	RunStatusRetryWaiting = runeffect.StatusRetryWaiting
	RunStatusSucceeded    = runeffect.StatusSucceeded
	RunStatusFailed       = runeffect.StatusFailed
	RunStatusSkipped      = runeffect.StatusSkipped
	RunStatusDuplicate    = runeffect.StatusDuplicate
)

// StoreOptions configures optional schedule store behavior.
type StoreOptions struct {
	// WorkspaceGroupID restricts scheduler list/recovery scans to the active
	// workspaces owned by one workspace profile group. Empty means unrestricted
	// monolith/management behavior.
	WorkspaceGroupID string
}

// Store owns schedule data access. It uses the schedule_definitions and
// automation_runs tables as the source of truth.
type Store struct {
	db               coredb.DB
	workspaceGroupID string
}

// New constructs a schedule Store.
func New(db coredb.DB) *Store {
	return NewWithOptions(db, StoreOptions{})
}

// NewWithOptions constructs a schedule Store with optional scheduler bounds.
func NewWithOptions(db coredb.DB, options StoreOptions) *Store {
	return &Store{db: db, workspaceGroupID: trim(options.WorkspaceGroupID)}
}

func (store *Store) schedulerWorkspaceGroupID() string {
	if store == nil {
		return ""
	}
	return trim(store.workspaceGroupID)
}

// Validate checks whether the store is wired.
func (store *Store) Validate() error {
	if store == nil || store.db == nil {
		return fmt.Errorf("schedule store database is not configured")
	}
	return nil
}

// InTx returns a transaction-bound schedule store.
func (store *Store) InTx(tx coredb.Tx) *TxStore {
	return &TxStore{db: tx}
}

// TxStore exposes schedule persistence helpers bound to an existing transaction.
type TxStore struct {
	db coredb.QueryExecer
}

func queryerFromStore(store *Store) (coredb.Queryer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("schedule store database is not configured")
	}
	return store.db, nil
}

func execerFromStore(store *Store) (coredb.QueryExecer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("schedule store database is not configured")
	}
	return store.db, nil
}

func execerFromTxStore(store *TxStore) (coredb.QueryExecer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("schedule tx store database is not configured")
	}
	return store.db, nil
}

func trim(value string) string { return strings.TrimSpace(value) }

func utcOrNow(t time.Time) time.Time {
	if t.IsZero() {
		return time.Now().UTC()
	}
	return t.UTC()
}

func nullableString(value string) any {
	trimmed := trim(value)
	if trimmed == "" {
		return nil
	}
	return trimmed
}

func nullableBytes(value []byte) any {
	if len(value) == 0 {
		return nil
	}
	return value
}
