package steme

import (
	"encoding/hex"
	"fmt"
)

// Assertion represents a knowledge graph assertion.
//
// This is the primary type for building assertions to submit to StemeDB.
// Use the fluent builder API with NewAssertion() for ergonomic construction.
type Assertion struct {
	// The subject entity (e.g., "Tesla_Inc")
	Subject string `json:"subject"`

	// The predicate/relation (e.g., "has_revenue")
	Predicate string `json:"predicate"`

	// The object value
	Object ObjectValue `json:"object"`

	// Confidence score (0.0 to 1.0)
	Confidence float64 `json:"confidence"`

	// Agent signatures vouching for this assertion
	Signatures []SignatureEntry `json:"signatures"`

	// Hash of parent assertion (hex-encoded, optional for forks)
	ParentHash *string `json:"parent_hash,omitempty"`

	// Hash of source evidence (hex-encoded)
	SourceHash string `json:"source_hash"`

	// Source authority tier (defaults to Expert if not specified)
	SourceClass *SourceClass `json:"source_class,omitempty"`

	// Perceptual hash for visual anchoring (hex-encoded, optional)
	VisualHash *string `json:"visual_hash,omitempty"`

	// Epoch ID (hex-encoded, optional)
	Epoch *string `json:"epoch,omitempty"`

	// Lifecycle stage (defaults to Proposed if not specified)
	Lifecycle *LifecycleStage `json:"lifecycle,omitempty"`

	// Semantic embedding vector (optional)
	Vector []float32 `json:"vector,omitempty"`
}

// AssertionBuilder provides a fluent API for building assertions.
type AssertionBuilder struct {
	assertion Assertion
}

// NewAssertion creates a new assertion builder with required fields.
//
// The subject and predicate are required. All other fields can be set
// via builder methods.
//
// Example:
//
//	assertion := steme.NewAssertion("Tesla_Inc", "has_revenue").
//	    WithNumber(96.7).
//	    WithConfidence(0.95).
//	    WithLifecycle(steme.LifecycleApproved).
//	    WithSourceClass(steme.SourceClassClinical)
func NewAssertion(subject, predicate string) *AssertionBuilder {
	return &AssertionBuilder{
		assertion: Assertion{
			Subject:    subject,
			Predicate:  predicate,
			Confidence: 1.0, // Default to full confidence
		},
	}
}

// WithText sets the object to a text value.
func (b *AssertionBuilder) WithText(text string) *AssertionBuilder {
	b.assertion.Object = NewTextValue(text)
	return b
}

// WithNumber sets the object to a numeric value.
func (b *AssertionBuilder) WithNumber(num float64) *AssertionBuilder {
	b.assertion.Object = NewNumberValue(num)
	return b
}

// WithBoolean sets the object to a boolean value.
func (b *AssertionBuilder) WithBoolean(val bool) *AssertionBuilder {
	b.assertion.Object = NewBooleanValue(val)
	return b
}

// WithReference sets the object to an entity reference.
func (b *AssertionBuilder) WithReference(entityID string) *AssertionBuilder {
	b.assertion.Object = NewReferenceValue(entityID)
	return b
}

// WithObject sets the object value directly.
func (b *AssertionBuilder) WithObject(obj ObjectValue) *AssertionBuilder {
	b.assertion.Object = obj
	return b
}

// WithConfidence sets the confidence score (0.0 to 1.0).
func (b *AssertionBuilder) WithConfidence(confidence float64) *AssertionBuilder {
	b.assertion.Confidence = confidence
	return b
}

// WithSourceHash sets the source evidence hash (hex-encoded, 32 bytes).
func (b *AssertionBuilder) WithSourceHash(hashHex string) *AssertionBuilder {
	b.assertion.SourceHash = hashHex
	return b
}

// WithSourceClass sets the source authority tier.
func (b *AssertionBuilder) WithSourceClass(sc SourceClass) *AssertionBuilder {
	b.assertion.SourceClass = &sc
	return b
}

// WithLifecycle sets the lifecycle stage.
func (b *AssertionBuilder) WithLifecycle(lifecycle LifecycleStage) *AssertionBuilder {
	b.assertion.Lifecycle = &lifecycle
	return b
}

// WithParentHash sets the parent assertion hash (hex-encoded, 32 bytes).
//
// This is used for forking existing assertions.
func (b *AssertionBuilder) WithParentHash(hashHex string) *AssertionBuilder {
	b.assertion.ParentHash = &hashHex
	return b
}

// WithVisualHash sets the perceptual hash (hex-encoded, 8 bytes).
//
// This is used for visual anchoring (e.g., screenshots of tables).
func (b *AssertionBuilder) WithVisualHash(hashHex string) *AssertionBuilder {
	b.assertion.VisualHash = &hashHex
	return b
}

// WithEpoch sets the epoch ID (hex-encoded, 32 bytes).
func (b *AssertionBuilder) WithEpoch(epochHex string) *AssertionBuilder {
	b.assertion.Epoch = &epochHex
	return b
}

// WithVector sets the semantic embedding vector.
func (b *AssertionBuilder) WithVector(vector []float32) *AssertionBuilder {
	b.assertion.Vector = vector
	return b
}

// WithSignatures sets the signature entries directly.
//
// This is used if you've pre-computed signatures. Most users should
// let the Client sign automatically.
func (b *AssertionBuilder) WithSignatures(sigs []SignatureEntry) *AssertionBuilder {
	b.assertion.Signatures = sigs
	return b
}

// Build returns the constructed Assertion.
//
// This does NOT validate the assertion. Validation happens on the server.
func (b *AssertionBuilder) Build() Assertion {
	return b.assertion
}

// Validate checks the assertion for common errors.
//
// Returns an error if:
//   - Confidence is not in [0.0, 1.0]
//   - Object value is not set
//   - SourceHash is empty
//   - Hex-encoded hashes have invalid lengths
func (a *Assertion) Validate() error {
	if a.Confidence < 0.0 || a.Confidence > 1.0 {
		return &ValidationError{
			Field:   "confidence",
			Message: fmt.Sprintf("must be between 0.0 and 1.0, got %f", a.Confidence),
		}
	}

	if a.Object.Type == "" {
		return &ValidationError{
			Field:   "object",
			Message: "object value must be set",
		}
	}

	if a.SourceHash == "" {
		return &ValidationError{
			Field:   "source_hash",
			Message: "source_hash is required",
		}
	}

	// Validate source_hash is 64 hex chars (32 bytes)
	if len(a.SourceHash) != 64 {
		return &ValidationError{
			Field:   "source_hash",
			Message: fmt.Sprintf("must be 64 hex characters (32 bytes), got %d", len(a.SourceHash)),
		}
	}

	if _, err := hex.DecodeString(a.SourceHash); err != nil {
		return &ValidationError{
			Field:   "source_hash",
			Message: fmt.Sprintf("invalid hex encoding: %v", err),
		}
	}

	// Validate parent_hash if present
	if a.ParentHash != nil {
		if len(*a.ParentHash) != 64 {
			return &ValidationError{
				Field:   "parent_hash",
				Message: fmt.Sprintf("must be 64 hex characters (32 bytes), got %d", len(*a.ParentHash)),
			}
		}

		if _, err := hex.DecodeString(*a.ParentHash); err != nil {
			return &ValidationError{
				Field:   "parent_hash",
				Message: fmt.Sprintf("invalid hex encoding: %v", err),
			}
		}
	}

	// Validate visual_hash if present (8 bytes = 16 hex chars)
	if a.VisualHash != nil {
		if len(*a.VisualHash) != 16 {
			return &ValidationError{
				Field:   "visual_hash",
				Message: fmt.Sprintf("must be 16 hex characters (8 bytes), got %d", len(*a.VisualHash)),
			}
		}

		if _, err := hex.DecodeString(*a.VisualHash); err != nil {
			return &ValidationError{
				Field:   "visual_hash",
				Message: fmt.Sprintf("invalid hex encoding: %v", err),
			}
		}
	}

	// Validate epoch if present
	if a.Epoch != nil {
		if len(*a.Epoch) != 64 {
			return &ValidationError{
				Field:   "epoch",
				Message: fmt.Sprintf("must be 64 hex characters (32 bytes), got %d", len(*a.Epoch)),
			}
		}

		if _, err := hex.DecodeString(*a.Epoch); err != nil {
			return &ValidationError{
				Field:   "epoch",
				Message: fmt.Sprintf("invalid hex encoding: %v", err),
			}
		}
	}

	return nil
}
