package policy

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/acp/interceptor/internal/models"
)

// Evaluator evaluates policy for a given input document.
type Evaluator interface {
	Evaluate(ctx context.Context, in models.PolicyInput) (models.PolicyDecision, error)
}

// OPAEvaluator adapts OPAClient to Evaluator by decoding the decision document.
type OPAEvaluator struct {
	client *OPAClient
}

// NewOPAEvaluator constructs an evaluator backed by OPA.
func NewOPAEvaluator(c *OPAClient) *OPAEvaluator {
	return &OPAEvaluator{client: c}
}

// Evaluate implements Evaluator.
func (e *OPAEvaluator) Evaluate(ctx context.Context, in models.PolicyInput) (models.PolicyDecision, error) {
	raw, err := e.client.Evaluate(ctx, in)
	if err != nil {
		return models.PolicyDecision{}, err
	}
	if len(raw) == 0 || string(raw) == "null" {
		return models.PolicyDecision{}, fmt.Errorf("opa returned empty result")
	}

	var asString string
	if err := json.Unmarshal(raw, &asString); err == nil {
		d := normalizeDecision(asString)
		if d == "" {
			return models.PolicyDecision{}, fmt.Errorf("invalid OPA decision string: %q", asString)
		}
		return models.PolicyDecision{Decision: d}, nil
	}

	var obj models.PolicyDecision
	if err := json.Unmarshal(raw, &obj); err != nil {
		return models.PolicyDecision{}, fmt.Errorf("decode OPA decision: %w", err)
	}
	obj.Decision = normalizeDecision(obj.Decision)
	if obj.Decision == "" {
		return models.PolicyDecision{}, fmt.Errorf("missing decision in OPA result: %s", string(raw))
	}
	return obj, nil
}

func normalizeDecision(s string) string {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "allow", "deny", "escalate":
		return strings.ToLower(strings.TrimSpace(s))
	default:
		return ""
	}
}
