package ucan

import (
	"github.com/ipld/go-ipld-prime"
	"code.sonr.org/go/ucan/pkg/policy"
	"code.sonr.org/go/ucan/pkg/policy/literal"
)

// PolicyBuilder provides a fluent API for constructing UCAN policies.
// Policies are arrays of statements that form an implicit AND.
type PolicyBuilder struct {
	constructors []policy.Constructor
}

// NewPolicy creates a new PolicyBuilder.
func NewPolicy() *PolicyBuilder {
	return &PolicyBuilder{
		constructors: make([]policy.Constructor, 0),
	}
}

// Build constructs the final Policy from all added statements.
func (b *PolicyBuilder) Build() (Policy, error) {
	return policy.Construct(b.constructors...)
}

// MustBuild constructs the final Policy, panicking on error.
func (b *PolicyBuilder) MustBuild() Policy {
	return policy.MustConstruct(b.constructors...)
}

// Equal adds an equality constraint: selector == value
func (b *PolicyBuilder) Equal(selector string, value any) *PolicyBuilder {
	node, err := toIPLDNode(value)
	if err != nil {
		// Store error for Build() to return
		b.constructors = append(b.constructors, func() (policy.Statement, error) {
			return nil, err
		})
		return b
	}
	b.constructors = append(b.constructors, policy.Equal(selector, node))
	return b
}

// NotEqual adds an inequality constraint: selector != value
func (b *PolicyBuilder) NotEqual(selector string, value any) *PolicyBuilder {
	node, err := toIPLDNode(value)
	if err != nil {
		b.constructors = append(b.constructors, func() (policy.Statement, error) {
			return nil, err
		})
		return b
	}
	b.constructors = append(b.constructors, policy.NotEqual(selector, node))
	return b
}

// GreaterThan adds a comparison constraint: selector > value
func (b *PolicyBuilder) GreaterThan(selector string, value int64) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.GreaterThan(selector, literal.Int(value)))
	return b
}

// GreaterThanOrEqual adds a comparison constraint: selector >= value
func (b *PolicyBuilder) GreaterThanOrEqual(selector string, value int64) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.GreaterThanOrEqual(selector, literal.Int(value)))
	return b
}

// LessThan adds a comparison constraint: selector < value
func (b *PolicyBuilder) LessThan(selector string, value int64) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.LessThan(selector, literal.Int(value)))
	return b
}

// LessThanOrEqual adds a comparison constraint: selector <= value
func (b *PolicyBuilder) LessThanOrEqual(selector string, value int64) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.LessThanOrEqual(selector, literal.Int(value)))
	return b
}

// Like adds a glob pattern constraint: selector matches pattern
// Use * for wildcard, \* for literal asterisk.
func (b *PolicyBuilder) Like(selector, pattern string) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.Like(selector, pattern))
	return b
}

// Not negates a statement
func (b *PolicyBuilder) Not(stmt policy.Constructor) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.Not(stmt))
	return b
}

// And adds a logical AND of multiple statements
func (b *PolicyBuilder) And(stmts ...policy.Constructor) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.And(stmts...))
	return b
}

// Or adds a logical OR of multiple statements
func (b *PolicyBuilder) Or(stmts ...policy.Constructor) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.Or(stmts...))
	return b
}

// All adds a universal quantifier: all elements at selector must satisfy statement
func (b *PolicyBuilder) All(selector string, stmt policy.Constructor) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.All(selector, stmt))
	return b
}

// Any adds an existential quantifier: at least one element at selector must satisfy statement
func (b *PolicyBuilder) Any(selector string, stmt policy.Constructor) *PolicyBuilder {
	b.constructors = append(b.constructors, policy.Any(selector, stmt))
	return b
}

// toIPLDNode converts a Go value to an IPLD node for policy evaluation.
func toIPLDNode(value any) (ipld.Node, error) {
	// Handle IPLD nodes directly
	if node, ok := value.(ipld.Node); ok {
		return node, nil
	}

	// Use literal package for conversion
	return literal.Any(value)
}

// --- Sonr-specific Policy Helpers ---

// VaultPolicy creates a policy that restricts operations to a specific vault.
// The vault is identified by its CID.
func VaultPolicy(vaultCID string) Policy {
	return NewPolicy().Equal(".vault", vaultCID).MustBuild()
}

// DIDPolicy creates a policy that restricts operations to DIDs matching a pattern.
// Use glob patterns: "did:sonr:*" matches all Sonr DIDs.
func DIDPolicy(didPattern string) Policy {
	return NewPolicy().Like(".did", didPattern).MustBuild()
}

// ChainPolicy creates a policy that restricts operations to a specific chain.
func ChainPolicy(chainID string) Policy {
	return NewPolicy().Equal(".chain_id", chainID).MustBuild()
}

// AccountPolicy creates a policy that restricts operations to a specific account address.
func AccountPolicy(address string) Policy {
	return NewPolicy().Equal(".address", address).MustBuild()
}

// RecordTypePolicy creates a policy for DWN operations on specific record types.
func RecordTypePolicy(recordType string) Policy {
	return NewPolicy().Equal(".record_type", recordType).MustBuild()
}

// CombinePolicies merges multiple policies into one (implicit AND).
func CombinePolicies(policies ...Policy) Policy {
	combined := make(Policy, 0)
	for _, p := range policies {
		combined = append(combined, p...)
	}
	return combined
}

// EmptyPolicy returns an empty policy (no constraints).
func EmptyPolicy() Policy {
	return Policy{}
}

// --- Policy Constructor Helpers ---

// These return policy.Constructor for use with And/Or/Not/All/Any

// EqualTo creates an equality constructor for nested policy building.
func EqualTo(selector string, value any) policy.Constructor {
	return func() (policy.Statement, error) {
		node, err := toIPLDNode(value)
		if err != nil {
			return nil, err
		}
		return policy.Equal(selector, node)()
	}
}

// NotEqualTo creates an inequality constructor for nested policy building.
func NotEqualTo(selector string, value any) policy.Constructor {
	return func() (policy.Statement, error) {
		node, err := toIPLDNode(value)
		if err != nil {
			return nil, err
		}
		return policy.NotEqual(selector, node)()
	}
}

// Matches creates a glob pattern constructor for nested policy building.
func Matches(selector, pattern string) policy.Constructor {
	return policy.Like(selector, pattern)
}

// GreaterThanValue creates a comparison constructor.
func GreaterThanValue(selector string, value int64) policy.Constructor {
	return policy.GreaterThan(selector, literal.Int(value))
}

// LessThanValue creates a comparison constructor.
func LessThanValue(selector string, value int64) policy.Constructor {
	return policy.LessThan(selector, literal.Int(value))
}
