package infra

import (
	"database/sql"
	"os"
	"testing"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

// createTestProposalsDB crea una BD en memoria con el schema minimo de proposals
// para los tests de ProposalFromFailure.
func createTestProposalsDB(t *testing.T) string {
	t.Helper()
	f, err := os.CreateTemp("", "proposals_test_*.db")
	if err != nil {
		t.Fatalf("create temp db: %v", err)
	}
	f.Close()
	path := f.Name()
	t.Cleanup(func() { os.Remove(path) })

	db, err := sql.Open("sqlite3", "file:"+path+"?_journal_mode=WAL")
	if err != nil {
		t.Fatalf("open temp db: %v", err)
	}
	defer db.Close()

	_, err = db.Exec(`CREATE TABLE IF NOT EXISTS proposals (
		id          TEXT PRIMARY KEY,
		kind        TEXT NOT NULL CHECK(kind IN ('new_function','new_type','improve_function','improve_type','new_pipeline')),
		target_id   TEXT NOT NULL DEFAULT '',
		title       TEXT NOT NULL,
		description TEXT NOT NULL DEFAULT '',
		evidence    TEXT NOT NULL DEFAULT '{}',
		status      TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','approved','rejected','implemented')),
		created_by  TEXT NOT NULL DEFAULT '',
		reviewed_by TEXT NOT NULL DEFAULT '',
		created_at  TEXT NOT NULL,
		updated_at  TEXT NOT NULL
	)`)
	if err != nil {
		t.Fatalf("create proposals table: %v", err)
	}
	return path
}

func TestProposalFromFailure(t *testing.T) {
	t.Run("no inserta nada cuando todos los checks pasan", func(t *testing.T) {
		dbPath := createTestProposalsDB(t)
		results := []CheckResult{
			{ID: "check-ok", Status: "pass", Severity: "critical"},
			{ID: "check-skip", Status: "skip", Severity: "warning"},
		}
		ids, err := ProposalFromFailure(dbPath, "app_test", results, "exec_001")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(ids) != 0 {
			t.Errorf("expected 0 proposals, got %d", len(ids))
		}
	})

	t.Run("inserta proposal por cada check fallido", func(t *testing.T) {
		dbPath := createTestProposalsDB(t)
		results := []CheckResult{
			{ID: "check-api", Status: "fail", Severity: "critical", ExitCode: 1, Error: "connection refused"},
			{ID: "check-perf", Status: "fail", Severity: "warning", ExitCode: 0, Stdout: "slow"},
			{ID: "check-ok", Status: "pass", Severity: "critical"},
		}
		ids, err := ProposalFromFailure(dbPath, "app_test", results, "exec_002")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(ids) != 2 {
			t.Errorf("expected 2 proposals, got %d: %v", len(ids), ids)
		}
	})

	t.Run("proposal critica usa kind new_function", func(t *testing.T) {
		dbPath := createTestProposalsDB(t)
		results := []CheckResult{
			{ID: "check-critical", Status: "fail", Severity: "critical", ExitCode: 2},
		}
		ids, err := ProposalFromFailure(dbPath, "app_x", results, "exec_003")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(ids) != 1 {
			t.Fatalf("expected 1 proposal, got %d", len(ids))
		}

		db, _ := sql.Open("sqlite3", "file:"+dbPath)
		defer db.Close()
		var kind, status, createdBy string
		err = db.QueryRow("SELECT kind, status, created_by FROM proposals WHERE id = ?", ids[0]).Scan(&kind, &status, &createdBy)
		if err != nil {
			t.Fatalf("query proposal: %v", err)
		}
		if kind != "new_function" {
			t.Errorf("expected kind=new_function, got %q", kind)
		}
		if status != "pending" {
			t.Errorf("expected status=pending, got %q", status)
		}
		if createdBy != "reactive_loop" {
			t.Errorf("expected created_by=reactive_loop, got %q", createdBy)
		}
	})

	t.Run("proposal warning usa kind improve_function", func(t *testing.T) {
		dbPath := createTestProposalsDB(t)
		results := []CheckResult{
			{ID: "check-warning", Status: "fail", Severity: "warning"},
		}
		ids, err := ProposalFromFailure(dbPath, "app_y", results, "exec_004")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(ids) != 1 {
			t.Fatalf("expected 1 proposal, got %d", len(ids))
		}

		db, _ := sql.Open("sqlite3", "file:"+dbPath)
		defer db.Close()
		var kind string
		_ = db.QueryRow("SELECT kind FROM proposals WHERE id = ?", ids[0]).Scan(&kind)
		if kind != "improve_function" {
			t.Errorf("expected kind=improve_function, got %q", kind)
		}
	})

	t.Run("proposals tienen timestamp reciente", func(t *testing.T) {
		dbPath := createTestProposalsDB(t)
		before := time.Now().UTC().Add(-time.Second)
		results := []CheckResult{
			{ID: "check-ts", Status: "fail", Severity: "critical"},
		}
		ids, err := ProposalFromFailure(dbPath, "app_z", results, "exec_005")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		after := time.Now().UTC().Add(time.Second)

		db, _ := sql.Open("sqlite3", "file:"+dbPath)
		defer db.Close()
		var createdAt string
		_ = db.QueryRow("SELECT created_at FROM proposals WHERE id = ?", ids[0]).Scan(&createdAt)
		ts, err := time.Parse(time.RFC3339, createdAt)
		if err != nil {
			t.Fatalf("parse created_at: %v", err)
		}
		if ts.Before(before) || ts.After(after) {
			t.Errorf("created_at %v out of expected range [%v, %v]", ts, before, after)
		}
	})
}
