package teamworkflow

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

const (
	defaultListLimit      = 50
	maxListLimit          = 101
	maxErrorCodeLength    = 128
	maxErrorMessageLength = 1024
	maxDigestLength       = 128
)

var (
	ErrNotFound   = storeerr.ErrNotFound
	ErrConflict   = storeerr.ErrConflict
	ErrBadRequest = storeerr.ErrBadRequest
)

type RevisionConflictError struct {
	Expected int64
	Actual   int64
}

func (err RevisionConflictError) Error() string {
	message := fmt.Sprintf("Team Workflow revision conflict: expected %d, actual %d", err.Expected, err.Actual)
	return message
}

func (RevisionConflictError) Unwrap() error { return ErrConflict }

type Store struct{ db coredb.DB }

type TxStore struct{ tx coredb.Tx }

func New(db coredb.DB) *Store { return &Store{db: db} }

func (store *Store) InTx(tx coredb.Tx) *TxStore { return &TxStore{tx: tx} }

func (store *Store) Validate() error {
	if store == nil || store.db == nil {
		return fmt.Errorf("Team Workflow store database is not configured")
	}
	return nil
}

func (store *TxStore) validate() error {
	if store == nil || store.tx == nil {
		return fmt.Errorf("Team Workflow transaction store is not configured")
	}
	return nil
}

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

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

func required(value string, label string, maxLength int) (string, error) {
	value = trim(value)
	if value == "" {
		return "", storeerr.BadRequest(label + " is required")
	}
	if maxLength > 0 && len(value) > maxLength {
		return "", storeerr.BadRequest(label + " is too long")
	}
	return value, nil
}

func positiveRevision(value int64, label string) error {
	if value <= 0 {
		return storeerr.BadRequest(label + " must be positive")
	}
	return nil
}

func normalizedListLimit(value int) int {
	if value <= 0 {
		return defaultListLimit
	}
	if value > maxListLimit {
		return maxListLimit
	}
	return value
}

func newTeamWorkflowID() (string, error) {
	id, err := identifiers.NewEntityID("twf")
	if err != nil {
		return "", fmt.Errorf("generate Team Workflow id: %w", err)
	}
	return id, nil
}

func ptrString(value sql.NullString) *string {
	if !value.Valid {
		return nil
	}
	copy := value.String
	return &copy
}

func ptrInt64(value sql.NullInt64) *int64 {
	if !value.Valid {
		return nil
	}
	copy := value.Int64
	return &copy
}

func ptrTime(value sql.NullTime) *time.Time {
	if !value.Valid {
		return nil
	}
	copy := value.Time.UTC()
	return &copy
}

func mapWriteError(err error) error {
	if err == nil {
		return nil
	}
	if coredb.IsNotFound(err) || errors.Is(err, sql.ErrNoRows) {
		return storeerr.NotFound("Team Workflow not found")
	}
	if coredb.IsUniqueViolation(err) {
		return storeerr.Conflict("Team Workflow already exists")
	}
	return err
}

func withTx(ctx context.Context, store *Store, run func(*TxStore) error) error {
	if err := store.Validate(); err != nil {
		return err
	}
	return coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		return run(store.InTx(tx))
	})
}
