package routing_test

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/veylant/ia-gateway/internal/routing"
)

func rctx() *routing.RoutingContext {
	return &routing.RoutingContext{
		TenantID:      "t1",
		UserRole:      "user",
		Department:    "finance",
		Sensitivity:   routing.SensitivityHigh,
		Model:         "gpt-4o",
		UseCase:       "summarization",
		TokenEstimate: 500,
	}
}

func cond(field, op string, val interface{}) routing.Condition {
	return routing.Condition{Field: field, Operator: op, Value: val}
}

// ─── eq / neq ────────────────────────────────────────────────────────────────

func TestCondition_Eq_Role_Match(t *testing.T) {
	assert.True(t, cond("user.role", "eq", "user").Evaluate(rctx()))
}

func TestCondition_Eq_Role_NoMatch(t *testing.T) {
	assert.False(t, cond("user.role", "eq", "admin").Evaluate(rctx()))
}

func TestCondition_Neq_Role(t *testing.T) {
	assert.True(t, cond("user.role", "neq", "admin").Evaluate(rctx()))
	assert.False(t, cond("user.role", "neq", "user").Evaluate(rctx()))
}

func TestCondition_Eq_Department(t *testing.T) {
	assert.True(t, cond("user.department", "eq", "finance").Evaluate(rctx()))
	assert.False(t, cond("user.department", "eq", "engineering").Evaluate(rctx()))
}

func TestCondition_Eq_Model(t *testing.T) {
	assert.True(t, cond("request.model", "eq", "gpt-4o").Evaluate(rctx()))
}

func TestCondition_Eq_UseCase(t *testing.T) {
	assert.True(t, cond("request.use_case", "eq", "summarization").Evaluate(rctx()))
}

// ─── in / nin ────────────────────────────────────────────────────────────────

func TestCondition_In_Role_Match(t *testing.T) {
	assert.True(t, cond("user.role", "in", []interface{}{"admin", "user"}).Evaluate(rctx()))
}

func TestCondition_In_Role_NoMatch(t *testing.T) {
	assert.False(t, cond("user.role", "in", []interface{}{"admin", "manager"}).Evaluate(rctx()))
}

func TestCondition_Nin_Department(t *testing.T) {
	assert.True(t, cond("user.department", "nin", []interface{}{"rh", "engineering"}).Evaluate(rctx()))
	assert.False(t, cond("user.department", "nin", []interface{}{"finance", "rh"}).Evaluate(rctx()))
}

// ─── sensitivity gte / lte ───────────────────────────────────────────────────

func TestCondition_Sensitivity_Gte_Match(t *testing.T) {
	// rctx sensitivity = high (3)
	assert.True(t, cond("request.sensitivity", "gte", "medium").Evaluate(rctx()))
	assert.True(t, cond("request.sensitivity", "gte", "high").Evaluate(rctx()))
	assert.False(t, cond("request.sensitivity", "gte", "critical").Evaluate(rctx()))
}

func TestCondition_Sensitivity_Lte_Match(t *testing.T) {
	assert.True(t, cond("request.sensitivity", "lte", "critical").Evaluate(rctx()))
	assert.True(t, cond("request.sensitivity", "lte", "high").Evaluate(rctx()))
	assert.False(t, cond("request.sensitivity", "lte", "medium").Evaluate(rctx()))
}

func TestCondition_Sensitivity_Eq(t *testing.T) {
	assert.True(t, cond("request.sensitivity", "eq", "high").Evaluate(rctx()))
	assert.False(t, cond("request.sensitivity", "eq", "critical").Evaluate(rctx()))
}

func TestCondition_Sensitivity_In(t *testing.T) {
	assert.True(t, cond("request.sensitivity", "in", []interface{}{"high", "critical"}).Evaluate(rctx()))
}

func TestCondition_Sensitivity_InvalidValue_ReturnsFalse(t *testing.T) {
	assert.False(t, cond("request.sensitivity", "gte", "invalid_level").Evaluate(rctx()))
}

// ─── token_estimate numeric ───────────────────────────────────────────────────

func TestCondition_TokenEstimate_Gte(t *testing.T) {
	assert.True(t, cond("request.token_estimate", "gte", float64(100)).Evaluate(rctx()))
	assert.True(t, cond("request.token_estimate", "gte", float64(500)).Evaluate(rctx()))
	assert.False(t, cond("request.token_estimate", "gte", float64(1000)).Evaluate(rctx()))
}

func TestCondition_TokenEstimate_Lte(t *testing.T) {
	assert.True(t, cond("request.token_estimate", "lte", float64(1000)).Evaluate(rctx()))
	assert.False(t, cond("request.token_estimate", "lte", float64(499)).Evaluate(rctx()))
}

// ─── contains / matches ───────────────────────────────────────────────────────

func TestCondition_Contains_Model(t *testing.T) {
	assert.True(t, cond("request.model", "contains", "4o").Evaluate(rctx()))
	assert.False(t, cond("request.model", "contains", "claude").Evaluate(rctx()))
}

func TestCondition_Matches_Model_Prefix(t *testing.T) {
	assert.True(t, cond("request.model", "matches", "gpt-").Evaluate(rctx()))
	assert.False(t, cond("request.model", "matches", "claude-").Evaluate(rctx()))
}

// ─── unknown field / operator ─────────────────────────────────────────────────

func TestCondition_UnknownField_ReturnsFalse(t *testing.T) {
	assert.False(t, cond("user.unknown", "eq", "x").Evaluate(rctx()))
}

func TestCondition_UnknownOperator_ReturnsFalse(t *testing.T) {
	assert.False(t, cond("user.role", "startswith", "u").Evaluate(rctx()))
}

// ─── ValidateConditions ───────────────────────────────────────────────────────

func TestValidateConditions_Valid(t *testing.T) {
	err := routing.ValidateConditions([]routing.Condition{
		{Field: "request.sensitivity", Operator: "gte", Value: "high"},
		{Field: "user.department", Operator: "eq", Value: "finance"},
	})
	require.NoError(t, err)
}

func TestValidateConditions_InvalidField(t *testing.T) {
	err := routing.ValidateConditions([]routing.Condition{
		{Field: "user.age", Operator: "eq", Value: "30"},
	})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "user.age")
}

func TestValidateConditions_InvalidOperator(t *testing.T) {
	err := routing.ValidateConditions([]routing.Condition{
		{Field: "user.role", Operator: "starts_with", Value: "ad"},
	})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "starts_with")
}
