package adk

import (
	"fmt"
	"time"

	"github.com/orchard9/stemedb-go/steme"
)

// parseLen converts a string lens name to steme.Lens.
func parseLen(lens string) steme.Lens {
	switch lens {
	case "recency":
		return steme.LensRecency
	case "consensus":
		return steme.LensConsensus
	case "authority":
		return steme.LensAuthority
	case "vote_aware_consensus":
		return steme.LensVoteAwareConsensus
	case "trust_aware_authority":
		return steme.LensTrustAwareAuthority
	default:
		return steme.LensConsensus // Default to consensus
	}
}

// parseLifecycle converts a string lifecycle stage to steme.LifecycleStage.
func parseLifecycle(lifecycle string) steme.LifecycleStage {
	switch lifecycle {
	case "proposed":
		return steme.LifecycleProposed
	case "under_review":
		return steme.LifecycleUnderReview
	case "approved":
		return steme.LifecycleApproved
	case "deprecated":
		return steme.LifecycleDeprecated
	case "rejected":
		return steme.LifecycleRejected
	default:
		return steme.LifecycleProposed // Default to proposed
	}
}

// parseSupersessionType converts a string supersession type to steme.SupersessionType.
func parseSupersessionType(t string) steme.SupersessionType {
	switch t {
	case "Invalidate", "invalidate":
		return steme.SupersessionInvalidate
	case "Temporal", "temporal":
		return steme.SupersessionTemporal
	case "Refinement", "refinement":
		return steme.SupersessionRefinement
	case "RequiresReview", "requires_review":
		return steme.SupersessionRequiresReview
	case "Additive", "additive":
		return steme.SupersessionAdditive
	default:
		return steme.SupersessionInvalidate // Default to invalidate
	}
}

// setObjectValue sets the object value on an assertion builder based on the input type.
func setObjectValue(builder *steme.AssertionBuilder, obj any) error {
	switch v := obj.(type) {
	case string:
		builder.WithText(v)
	case float64:
		builder.WithNumber(v)
	case bool:
		builder.WithBoolean(v)
	case map[string]any:
		// Handle ObjectValue from JSON
		if typeStr, ok := v["type"].(string); ok {
			if value, ok := v["value"]; ok {
				switch typeStr {
				case "Text":
					if str, ok := value.(string); ok {
						builder.WithText(str)
					}
				case "Number":
					if num, ok := value.(float64); ok {
						builder.WithNumber(num)
					}
				case "Boolean":
					if b, ok := value.(bool); ok {
						builder.WithBoolean(b)
					}
				case "Reference":
					if ref, ok := value.(string); ok {
						builder.WithReference(ref)
					}
				}
			}
		}
	default:
		return fmt.Errorf("unsupported object type: %T", obj)
	}
	return nil
}

// convertQueryResult converts a steme.QueryResult to a QueryOutput.
func convertQueryResult(result *steme.QueryResult, minConfidence float32) QueryOutput {
	if len(result.Assertions) == 0 {
		return QueryOutput{
			Error: "no results found",
		}
	}

	// Take the first result (lens already applied resolution)
	assertion := result.Assertions[0]

	// Check confidence threshold
	confidence := float32(assertion.Confidence)
	if minConfidence > 0 && confidence < minConfidence {
		return QueryOutput{
			Value:      assertion.Object.Value,
			Confidence: confidence,
			Lifecycle:  string(assertion.Lifecycle),
			QueryID:    generateQueryID(),
			Error:      fmt.Sprintf("confidence %.2f below threshold %.2f", confidence, minConfidence),
		}
	}

	// Convert sources
	sources := make([]Source, 0, len(result.Assertions))
	for _, a := range result.Assertions {
		sources = append(sources, Source{
			Hash:       a.Hash,
			SourceHash: a.SourceHash,
			Weight:     float32(a.Confidence),
		})
	}

	return QueryOutput{
		Value:      assertion.Object.Value,
		Confidence: confidence,
		Lifecycle:  string(assertion.Lifecycle),
		Sources:    sources,
		QueryID:    generateQueryID(),
	}
}

// convertToConstraints converts query results to constraint objects.
func convertToConstraints(result *steme.QueryResult) []Constraint {
	constraints := make([]Constraint, 0)

	for _, assertion := range result.Assertions {
		constraint := Constraint{
			Subject: assertion.Subject,
		}

		// Determine if this is a must-use or forbidden constraint
		// based on predicate
		switch assertion.Predicate {
		case "must_use":
			if text, ok := assertion.Object.Value.(string); ok {
				constraint.MustUse = text
			}
		case "forbidden":
			if text, ok := assertion.Object.Value.(string); ok {
				constraint.Forbidden = text
			}
		}

		// Extract reason from parent assertion if available
		// (This is a simplified implementation)
		constraint.Reason = "Approved organizational pattern"

		constraints = append(constraints, constraint)
	}

	return constraints
}

// generateQueryID generates a unique query ID for audit trails.
func generateQueryID() string {
	// Generate a unique query ID for audit trail
	// In production, this would be generated server-side
	return fmt.Sprintf("query_%d", time.Now().UnixNano())
}

// convertTraceResult converts a steme.TraceResult to a TraceOutput.
func convertTraceResult(result *steme.TraceResult) TraceOutput {
	queries := make([]QueryTrace, 0, len(result.Audits))

	for _, audit := range result.Audits {
		// Extract subject and predicate from params
		subject := ""
		if audit.Params.Subject != nil {
			subject = *audit.Params.Subject
		}

		predicate := ""
		if audit.Params.Predicate != nil {
			predicate = *audit.Params.Predicate
		}

		// Extract lens (convert to lowercase for consistency)
		lens := "consensus" // Default
		if audit.Params.Lens != nil {
			lensVal := string(*audit.Params.Lens)
			// Convert "Consensus" -> "consensus", etc.
			switch lensVal {
			case "Recency":
				lens = "recency"
			case "Consensus":
				lens = "consensus"
			case "Authority":
				lens = "authority"
			case "VoteAwareConsensus":
				lens = "vote_aware_consensus"
			case "TrustAwareAuthority":
				lens = "trust_aware_authority"
			default:
				lens = "consensus"
			}
		}

		// Extract result value (simplified - just use hash if available)
		resultValue := ""
		if audit.ResultHash != nil {
			resultValue = *audit.ResultHash
		}

		// Extract contributing assertion hashes
		contributing := make([]string, 0, len(audit.ContributingAssertions))
		for _, ca := range audit.ContributingAssertions {
			contributing = append(contributing, ca.AssertionHash)
		}

		queries = append(queries, QueryTrace{
			QueryID:      audit.QueryID,
			Timestamp:    fmt.Sprintf("%d", audit.Timestamp),
			Subject:      subject,
			Predicate:    predicate,
			Lens:         lens,
			Result:       resultValue,
			Confidence:   audit.ResultConfidence,
			Contributing: contributing,
		})
	}

	return TraceOutput{
		Queries: queries,
	}
}
