package postgres

import (
	"context"
	"encoding/json"
	"time"

	"github.com/acp/interceptor/internal/audit"
	"github.com/jackc/pgx/v5/pgxpool"
)

// AuditStore persists governance events in Postgres.
type AuditStore struct {
	pool *pgxpool.Pool
}

// NewAuditStore creates a Postgres-backed audit store.
func NewAuditStore(pool *pgxpool.Pool) *AuditStore {
	return &AuditStore{pool: pool}
}

// Add inserts one audit event.
func (s *AuditStore) Add(e audit.Event) {
	if e.Timestamp.IsZero() {
		e.Timestamp = time.Now().UTC()
	}
	roles, _ := json.Marshal(e.PrincipalRoles)
	if roles == nil {
		roles = []byte("[]")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_, _ = s.pool.Exec(ctx, `
		INSERT INTO audit_events (
			type, timestamp, trace_id, agent_id, tool, action, decision, reason, policy,
			amount, risk_score, http_path, http_method, principal_sub, principal_roles
		) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)`,
		nullStr(e.Type, audit.ToolCallEvaluated),
		e.Timestamp,
		e.TraceID,
		e.AgentID,
		e.Tool,
		e.Action,
		e.Decision,
		e.Reason,
		e.Policy,
		e.Amount,
		e.RiskScore,
		e.HTTPPath,
		e.HTTPMethod,
		e.PrincipalSub,
		roles,
	)
}

// List returns recent events, newest first.
func (s *AuditStore) List(limit int) []audit.Event {
	if limit < 1 {
		limit = 50
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	rows, err := s.pool.Query(ctx, `
		SELECT type, timestamp, trace_id, agent_id, tool, action, decision, reason, policy,
		       amount, risk_score, http_path, http_method, principal_sub, principal_roles
		FROM audit_events
		ORDER BY timestamp DESC
		LIMIT $1`, limit)
	if err != nil {
		return nil
	}
	defer rows.Close()
	var out []audit.Event
	for rows.Next() {
		var e audit.Event
		var roles []byte
		if err := rows.Scan(
			&e.Type, &e.Timestamp, &e.TraceID, &e.AgentID, &e.Tool, &e.Action,
			&e.Decision, &e.Reason, &e.Policy, &e.Amount, &e.RiskScore,
			&e.HTTPPath, &e.HTTPMethod, &e.PrincipalSub, &roles,
		); err != nil {
			continue
		}
		_ = json.Unmarshal(roles, &e.PrincipalRoles)
		out = append(out, e)
	}
	return out
}

// Metrics aggregates decision counts from Postgres.
func (s *AuditStore) Metrics() audit.Summary {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	var sum audit.Summary
	err := s.pool.QueryRow(ctx, `
		SELECT
			COUNT(*)::int,
			COUNT(*) FILTER (WHERE decision = 'allow')::int,
			COUNT(*) FILTER (WHERE decision = 'deny')::int,
			COUNT(*) FILTER (WHERE decision = 'escalate')::int,
			COUNT(DISTINCT agent_id) FILTER (WHERE agent_id <> '')::int,
			COUNT(*) FILTER (WHERE risk_score > 0.8)::int
		FROM audit_events`).Scan(
		&sum.TotalEvents,
		&sum.AllowCount,
		&sum.DenyCount,
		&sum.EscalateCount,
		&sum.ActiveAgents,
		&sum.HighRiskCount,
	)
	if err != nil {
		return sum
	}
	if sum.TotalEvents > 0 {
		sum.AllowPct = float64(sum.AllowCount) / float64(sum.TotalEvents) * 100
		sum.DenyPct = float64(sum.DenyCount) / float64(sum.TotalEvents) * 100
		sum.EscalatePct = float64(sum.EscalateCount) / float64(sum.TotalEvents) * 100
	}
	return sum
}

func nullStr(v, def string) string {
	if v == "" {
		return def
	}
	return v
}
