// Package audit produces read-only drift reports over the knowledge graph and
// monitoring state. It is the shared engine behind the
// /api/v1/audit/drift endpoint and the audit_knowledge_graph MCP tool.
//
// It surfaces the structural gaps an operator otherwise discovers only by
// accident: orphan check entities, checks targeting retired entities, probes
// stuck down/unknown, unmonitored declared types, and live edges pointing at
// destroyed/deprecated targets. Live-infra discovery (pct/docker/certs) is a
// follow-up that needs host-hop execution; these categories are pure DB
// queries, so the report is cheap, safe to run unattended, and testable.
package audit

import (
	"context"

	"github.com/dtoro/oikos/internal/adapters/postgres"
)

// Finding is one drift item the operator should look at.
type Finding struct {
	Category         string   `json:"category"`
	Severity         string   `json:"severity"` // info | warning | critical
	Count            int      `json:"count"`
	Entities         []string `json:"entities"`
	Evidence         string   `json:"evidence"`
	SuggestedRunbook string   `json:"suggested_runbook"`
}

// Summary tallies findings by category.
type Summary struct {
	TotalFindings int            `json:"total_findings"`
	ByCategory    map[string]int `json:"by_category"`
}

// Report runs every drift check and returns the findings plus a summary.
func Report(ctx context.Context, pool *db.Pool) ([]Finding, Summary) {
	specs := []struct {
		finding Finding
		query   string
	}{
		{
			Finding{Category: "orphan_checks", Severity: "warning",
				Evidence:         "check entities with truncated/random slugs (legacy shortSlug bug), no live target",
				SuggestedRunbook: "scripts/cleanup-orphan-checks.sh"},
			`SELECT e.slug FROM entities e
			 WHERE e.type = 'check'
			   AND e.slug ~ '^check:(ping|ssh-script|disk):[0-9a-f]{8}$'`,
		},
		{
			Finding{Category: "dead_checks", Severity: "warning",
				Evidence:         "enabled check_defs whose target entity is deprecated/destroyed",
				SuggestedRunbook: "lifecycle-deprecate-node / lifecycle-destroy-node"},
			`SELECT e.slug FROM check_defs cd
			 JOIN entities e ON e.id = cd.entity_id
			 JOIN entities tgt ON tgt.id = cd.target_id
			 WHERE cd.enabled AND tgt.state IN ('deprecated','destroyed')`,
		},
		{
			Finding{Category: "down_checks", Severity: "critical",
				Evidence:         "enabled checks reporting health=down",
				SuggestedRunbook: "service-health-check"},
			`SELECT e.slug FROM check_defs cd JOIN entities e ON e.id = cd.entity_id
			 WHERE cd.enabled AND cd.last_health = 'down'`,
		},
		{
			Finding{Category: "unknown_checks", Severity: "warning",
				Evidence:         "enabled checks that ran but reported health=unknown (likely misconfigured probe)",
				SuggestedRunbook: "knowledge-graph-audit"},
			`SELECT e.slug FROM check_defs cd JOIN entities e ON e.id = cd.entity_id
			 WHERE cd.enabled AND cd.last_health = 'unknown'`,
		},
		{
			Finding{Category: "unmonitored", Severity: "warning",
				Evidence:         "active entities whose type declares monitoring but have no enabled check_def",
				SuggestedRunbook: "knowledge-graph-audit"},
			`SELECT DISTINCT e.slug FROM signals sg
			 JOIN entities e ON e.id = sg.target_entity_id
			 WHERE sg.kind = 'unmonitored' AND sg.state IN ('raised','acknowledged','acting')`,
		},
		{
			Finding{Category: "dangling_edges", Severity: "warning",
				Evidence:         "live relationships (hosts/provides/mounts) pointing at destroyed/deprecated targets",
				SuggestedRunbook: "lifecycle-destroy-node"},
			`SELECT src.slug || ' -' || r.type || '-> ' || tgt.slug FROM relationships r
			 JOIN entities src ON src.id = r.source_id
			 JOIN entities tgt ON tgt.id = r.target_id
			 WHERE r.valid_to IS NULL
			   AND src.state NOT IN ('destroyed','deprecated')
			   AND tgt.state IN ('destroyed','deprecated')`,
		},
		{
			Finding{Category: "polluted_attrs", Severity: "warning",
				Evidence:         "routing-critical attributes carrying prose (breaks resolution) — e.g. host='hubris (confirmed via pct…')",
				SuggestedRunbook: "knowledge-graph-audit"},
			`SELECT slug || ': host=' || (attributes->>'host') FROM entities
			 WHERE attributes->>'host' IS NOT NULL
			   AND (attributes->>'host') ~ '[ (]'`,
		},
	}

	findings := make([]Finding, 0, len(specs))
	summary := Summary{ByCategory: map[string]int{}}
	for _, sp := range specs {
		f := runFinding(ctx, pool, sp.finding, sp.query)
		findings = append(findings, f)
		summary.TotalFindings += f.Count
		summary.ByCategory[f.Category] = f.Count
	}
	return findings, summary
}

const entityCap = 50

// runFinding runs a single-column slug query and folds the rows into a Finding.
func runFinding(ctx context.Context, pool *db.Pool, f Finding, query string) Finding {
	rows, err := pool.Query(ctx, query)
	if err != nil {
		f.Evidence = f.Evidence + " (query error: " + err.Error() + ")"
		return f
	}
	defer rows.Close()
	for rows.Next() {
		var slug string
		if err := rows.Scan(&slug); err != nil {
			continue
		}
		f.Count++
		if len(f.Entities) < entityCap {
			f.Entities = append(f.Entities, slug)
		}
	}
	return f
}
