package approval

import (
	"context"
	"errors"
	"fkteams/agentcore"
	"fmt"
	"path/filepath"
	"strings"
)

const (
	StoreCommand  = "command"
	StoreFile     = "file"
	StoreGit      = "git"
	StoreDispatch = "dispatch"
)

const (
	Reject      = 0
	ApproveOnce = 1
	ApproveItem = 2
	ApproveAll  = 3
)

var ErrRejected = errors.New("user rejected the operation")

type OperationDetail struct {
	Name  string
	Value string
}

type Operation struct {
	StoreName string
	Key       string
	Title     string
	Target    string
	Details   []OperationDetail
}

type MatchFunc func(key string, approved map[string]bool) bool

func DirMatchFunc(key string, approved map[string]bool) bool {
	dir := key
	for {
		if approved[dir] {
			return true
		}
		parent := filepath.Dir(dir)
		if parent == dir {
			break
		}
		dir = parent
	}
	return false
}

type Store struct {
	items   map[string]bool
	all     bool
	matcher MatchFunc
}

func newStore(matcher MatchFunc) *Store {
	return &Store{items: make(map[string]bool), matcher: matcher}
}

func (s *Store) IsApproved(key string) bool {
	if s.all {
		return true
	}
	if s.matcher != nil {
		return s.matcher(key, s.items)
	}
	return s.items[key]
}

func (s *Store) approve(key string) { s.items[key] = true }
func (s *Store) setApproveAll()     { s.all = true }

type Registry struct {
	stores map[string]*Store
}

type StoreConfig struct {
	Name    string
	Matcher MatchFunc
}

func DefaultStoreConfigs() []StoreConfig {
	return []StoreConfig{
		{Name: StoreCommand},
		{Name: StoreFile, Matcher: DirMatchFunc},
		{Name: StoreGit, Matcher: DirMatchFunc},
		{Name: StoreDispatch},
	}
}

func NewRegistry(configs ...StoreConfig) *Registry {
	r := &Registry{stores: make(map[string]*Store, len(configs))}
	for _, c := range configs {
		r.stores[c.Name] = newStore(c.Matcher)
	}
	return r
}

func (r *Registry) get(name string) *Store { return r.stores[name] }

func NewDefaultRegistry() *Registry {
	return NewRegistry(DefaultStoreConfigs()...)
}

// NewAutoApproveRegistry 创建一个所有操作均自动审批通过的 Registry。
// 用于子任务等无需人工确认的自主执行场景。
func NewAutoApproveRegistry() *Registry {
	r := NewDefaultRegistry()
	for _, s := range r.stores {
		s.setApproveAll()
	}
	return r
}

func NewDefaultSelectiveRegistry(autoApprove []string) *Registry {
	return NewSelectiveRegistry(autoApprove, DefaultStoreConfigs()...)
}

// NewSelectiveRegistry 创建选择性自动批准的 Registry。
// autoApprove 中的 store 会被标记为全部通过，其余需审批。
// 传入 "all" 等同于 NewAutoApproveRegistry。
func NewSelectiveRegistry(autoApprove []string, configs ...StoreConfig) *Registry {
	r := NewRegistry(configs...)
	for _, name := range autoApprove {
		if name == "all" {
			for _, s := range r.stores {
				s.setApproveAll()
			}
			return r
		}
		if s, ok := r.stores[name]; ok {
			s.setApproveAll()
		}
	}
	return r
}

type registryCtxKey struct{}

func WithRegistry(ctx context.Context, reg *Registry) context.Context {
	return context.WithValue(ctx, registryCtxKey{}, reg)
}

func RegistryContext(reg *Registry) func(context.Context) context.Context {
	return func(ctx context.Context) context.Context {
		return WithRegistry(ctx, reg)
	}
}

func getStore(ctx context.Context, name string) *Store {
	if reg, ok := ctx.Value(registryCtxKey{}).(*Registry); ok {
		return reg.get(name)
	}
	return nil
}

func Require(ctx context.Context, storeName, key, info string) error {
	store := getStore(ctx, storeName)

	if store != nil && store.IsApproved(key) {
		return nil
	}

	wasInterrupted, _, _ := agentcore.GetInterruptState(ctx)
	if wasInterrupted {
		isTarget, hasData, decision := agentcore.GetResumeContext[int](ctx)
		if !isTarget {
			return agentcore.RequestInterrupt(ctx, nil)
		}
		if hasData {
			switch decision {
			case ApproveOnce:
				return nil
			case ApproveItem:
				if store != nil {
					store.approve(key)
				}
				return nil
			case ApproveAll:
				if store != nil {
					store.setApproveAll()
				}
				return nil
			}
		}
		return ErrRejected
	}

	return agentcore.RequestInterrupt(ctx, info)
}

// RequireOperation 使用统一格式发起一次人工审批。
func RequireOperation(ctx context.Context, op Operation) error {
	return Require(ctx, op.StoreName, op.Key, op.Info())
}

func (op Operation) Info() string {
	title := op.Title
	if title == "" {
		title = "Operation requires approval"
	}

	var sb strings.Builder
	fmt.Fprintf(&sb, "%s", title)
	if op.Target != "" {
		fmt.Fprintf(&sb, "\n  Target: %s", op.Target)
	}
	for _, detail := range op.Details {
		if detail.Name == "" || detail.Value == "" {
			continue
		}
		fmt.Fprintf(&sb, "\n  %s: %s", detail.Name, detail.Value)
	}
	return sb.String()
}

func RejectedMessage(err error, message string) (string, bool) {
	if errors.Is(err, ErrRejected) {
		if message == "" {
			message = ErrRejected.Error()
		}
		return message, true
	}
	return "", false
}
