package infra

import (
	"os"
	"path/filepath"
	"testing"

	"database/sql"

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

func seedTestRegistry(t *testing.T) string {
	t.Helper()
	dir := t.TempDir()
	dbPath := filepath.Join(dir, "registry.db")

	db, err := sql.Open("sqlite3", dbPath)
	if err != nil {
		t.Fatalf("open temp db: %v", err)
	}
	defer db.Close()

	_, err = db.Exec(`
		CREATE TABLE functions (
			id         TEXT PRIMARY KEY,
			name       TEXT NOT NULL,
			lang       TEXT NOT NULL,
			domain     TEXT NOT NULL,
			tags       TEXT NOT NULL DEFAULT '[]',
			kind       TEXT NOT NULL DEFAULT 'function',
			updated_at TEXT NOT NULL,
			uses_functions TEXT NOT NULL DEFAULT '[]'
		);
		CREATE TABLE apps (
			id             TEXT PRIMARY KEY,
			uses_functions TEXT NOT NULL DEFAULT '[]'
		);
		CREATE TABLE analysis (
			id             TEXT PRIMARY KEY,
			uses_functions TEXT NOT NULL DEFAULT '[]'
		);
	`)
	if err != nil {
		t.Fatalf("create schema: %v", err)
	}

	// fn_a is used by fn_b
	// fn_b is used by an app
	// fn_c is the orphan — nobody uses it
	_, err = db.Exec(`
		INSERT INTO functions VALUES
			('fn_a', 'fn_a', 'go', 'core', '[]', 'function', '2026-01-01T00:00:00Z', '[]'),
			('fn_b', 'fn_b', 'go', 'core', '[]', 'function', '2026-01-15T00:00:00Z', '["fn_a"]'),
			('fn_c', 'fn_c', 'go', 'core', '[]', 'function', '2025-06-01T00:00:00Z', '[]');
		INSERT INTO apps VALUES
			('app_x', '["fn_b"]');
		INSERT INTO analysis VALUES
			('an_y', '[]');
	`)
	if err != nil {
		t.Fatalf("seed data: %v", err)
	}

	return dir
}

func TestFindUnusedFunctions_DetectsOrphan(t *testing.T) {
	t.Run("solo fn_c queda huerfana con 2 funciones consumidas", func(t *testing.T) {
		dir := seedTestRegistry(t)
		got, err := FindUnusedFunctions(dir)
		if err != nil {
			t.Fatalf("FindUnusedFunctions error: %v", err)
		}

		if len(got) != 1 {
			ids := make([]string, len(got))
			for i, u := range got {
				ids[i] = u.ID
			}
			t.Fatalf("expected 1 unused function, got %d: %v", len(got), ids)
		}
		if got[0].ID != "fn_c" {
			t.Errorf("expected orphan ID fn_c, got %s", got[0].ID)
		}
		if got[0].AgeDays <= 0 {
			t.Errorf("expected positive AgeDays, got %d", got[0].AgeDays)
		}
	})

	t.Run("launcher pipeline se excluye aunque nadie la use", func(t *testing.T) {
		dir := t.TempDir()
		dbPath := filepath.Join(dir, "registry.db")
		db, err := sql.Open("sqlite3", dbPath)
		if err != nil {
			t.Fatalf("open db: %v", err)
		}
		defer db.Close()

		_, err = db.Exec(`
			CREATE TABLE functions (
				id TEXT PRIMARY KEY, name TEXT, lang TEXT, domain TEXT,
				tags TEXT DEFAULT '[]', kind TEXT DEFAULT 'function',
				updated_at TEXT, uses_functions TEXT DEFAULT '[]'
			);
			CREATE TABLE apps (id TEXT PRIMARY KEY, uses_functions TEXT DEFAULT '[]');
			CREATE TABLE analysis (id TEXT PRIMARY KEY, uses_functions TEXT DEFAULT '[]');
		`)
		if err != nil {
			t.Fatalf("schema: %v", err)
		}
		db.Exec(`
			INSERT INTO functions VALUES
				('pipe_launch', 'pipe_launch', 'bash', 'pipelines', '["launcher"]', 'pipeline', '2026-01-01T00:00:00Z', '[]'),
				('pipe_nolabel', 'pipe_nolabel', 'go', 'pipelines', '[]', 'pipeline', '2026-01-01T00:00:00Z', '[]'),
				('fn_orphan', 'fn_orphan', 'go', 'core', '[]', 'function', '2026-01-01T00:00:00Z', '[]');
		`)

		got, err := FindUnusedFunctions(dir)
		if err != nil {
			t.Fatalf("error: %v", err)
		}

		ids := map[string]bool{}
		for _, u := range got {
			ids[u.ID] = true
		}
		if ids["pipe_launch"] {
			t.Error("launcher pipeline should be excluded from unused")
		}
		if !ids["pipe_nolabel"] {
			t.Error("pipeline without launcher tag should appear as unused")
		}
		if !ids["fn_orphan"] {
			t.Error("orphan function should appear as unused")
		}
	})
}

func TestFindUnusedFunctions_MissingDB(t *testing.T) {
	t.Run("error si registry.db no existe", func(t *testing.T) {
		dir, _ := os.MkdirTemp("", "nodb")
		defer os.RemoveAll(dir)
		_, err := FindUnusedFunctions(dir)
		if err == nil {
			t.Error("expected error for missing db, got nil")
		}
	})
}
