//go:build gonavi_duckdb_driver

package db

import (
	"path/filepath"
	"strings"
	"testing"

	"GoNavi-Wails/internal/connection"
)

func TestDuckDBMetadataDetectsPrimaryAndUniqueIndexes(t *testing.T) {
	t.Parallel()

	dbPath := filepath.Join(t.TempDir(), "metadata.duckdb")
	client := &DuckDB{}
	if err := client.Connect(connection.ConnectionConfig{Type: "duckdb", Host: dbPath}); err != nil {
		t.Fatalf("Connect failed: %v", err)
	}
	t.Cleanup(func() {
		_ = client.Close()
	})

	if _, err := client.Exec(`
CREATE TABLE events (
	id BIGINT PRIMARY KEY,
	email VARCHAR UNIQUE,
	name VARCHAR
);
CREATE UNIQUE INDEX idx_events_name ON events(name);
`); err != nil {
		t.Fatalf("create test table failed: %v", err)
	}

	columns, err := client.GetColumns("main", "main.events")
	if err != nil {
		t.Fatalf("GetColumns failed: %v", err)
	}
	if len(columns) != 3 {
		t.Fatalf("unexpected column count: %d, columns=%+v", len(columns), columns)
	}

	keysByName := map[string]string{}
	for _, column := range columns {
		keysByName[column.Name] = column.Key
	}
	if keysByName["id"] != "PRI" {
		t.Fatalf("primary key metadata missing: columns=%+v", columns)
	}
	if keysByName["email"] != "UNI" {
		t.Fatalf("unique constraint metadata missing: columns=%+v", columns)
	}

	indexes, err := client.GetIndexes("main", "main.events")
	if err != nil {
		t.Fatalf("GetIndexes failed: %v", err)
	}
	if !duckDBTestHasUniqueIndexColumn(indexes, "id") {
		t.Fatalf("primary key index metadata missing: indexes=%+v", indexes)
	}
	if !duckDBTestHasUniqueIndexColumn(indexes, "email") {
		t.Fatalf("unique constraint index metadata missing: indexes=%+v", indexes)
	}
	if !duckDBTestHasUniqueIndexColumn(indexes, "name") {
		t.Fatalf("unique index metadata missing: indexes=%+v", indexes)
	}
}

func TestDuckDBDefinitionReloadReflectsLatestDDL(t *testing.T) {
	t.Parallel()

	dbPath := filepath.Join(t.TempDir(), "definition-reload.duckdb")
	client := &DuckDB{}
	if err := client.Connect(connection.ConnectionConfig{Type: "duckdb", Host: dbPath}); err != nil {
		t.Fatalf("Connect failed: %v", err)
	}
	t.Cleanup(func() {
		_ = client.Close()
	})

	if _, err := client.Exec(`
CREATE VIEW active_users AS
SELECT id FROM (VALUES (1), (2)) AS users(id);

CREATE OR REPLACE MACRO add_one(x) AS x + 1;
`); err != nil {
		t.Fatalf("create initial objects failed: %v", err)
	}

	viewDefinitionBefore, _, err := client.Query(`SELECT view_definition FROM information_schema.views WHERE table_schema = 'main' AND table_name = 'active_users' LIMIT 1`)
	if err != nil {
		t.Fatalf("query initial view definition failed: %v", err)
	}
	if len(viewDefinitionBefore) != 1 {
		t.Fatalf("expected one initial view definition row, got %+v", viewDefinitionBefore)
	}
	if got := duckDBRowString(viewDefinitionBefore[0], "view_definition"); !strings.Contains(got, "SELECT id FROM") || !strings.Contains(got, "VALUES (1), (2)") {
		t.Fatalf("unexpected initial view definition: %q", got)
	}

	routineDefinitionBefore, _, err := client.Query(`SELECT macro_definition FROM duckdb_functions() WHERE internal = false AND lower(function_type) = 'macro' AND schema_name = 'main' AND function_name = 'add_one' LIMIT 1`)
	if err != nil {
		t.Fatalf("query initial macro definition failed: %v", err)
	}
	if len(routineDefinitionBefore) != 1 {
		t.Fatalf("expected one initial macro definition row, got %+v", routineDefinitionBefore)
	}
	if got := duckDBRowString(routineDefinitionBefore[0], "macro_definition"); !strings.Contains(got, "x + 1") {
		t.Fatalf("unexpected initial macro definition: %q", got)
	}

	if _, err := client.Exec(`
CREATE OR REPLACE VIEW active_users AS
SELECT id, id * 10 AS score FROM (VALUES (1), (2)) AS users(id);

CREATE OR REPLACE MACRO add_one(x) AS x + 2;
`); err != nil {
		t.Fatalf("replace latest objects failed: %v", err)
	}

	viewDefinitionAfter, _, err := client.Query(`SELECT view_definition FROM information_schema.views WHERE table_schema = 'main' AND table_name = 'active_users' LIMIT 1`)
	if err != nil {
		t.Fatalf("query latest view definition failed: %v", err)
	}
	if len(viewDefinitionAfter) != 1 {
		t.Fatalf("expected one latest view definition row, got %+v", viewDefinitionAfter)
	}
	if got := duckDBRowString(viewDefinitionAfter[0], "view_definition"); !strings.Contains(got, "SELECT id") || !strings.Contains(got, "score") || !strings.Contains(got, "10") {
		t.Fatalf("expected latest view definition, got %q", got)
	}

	routineDefinitionAfter, _, err := client.Query(`SELECT macro_definition FROM duckdb_functions() WHERE internal = false AND lower(function_type) = 'macro' AND schema_name = 'main' AND function_name = 'add_one' LIMIT 1`)
	if err != nil {
		t.Fatalf("query latest macro definition failed: %v", err)
	}
	if len(routineDefinitionAfter) != 1 {
		t.Fatalf("expected one latest macro definition row, got %+v", routineDefinitionAfter)
	}
	if got := duckDBRowString(routineDefinitionAfter[0], "macro_definition"); !strings.Contains(got, "x + 2") {
		t.Fatalf("expected latest macro definition, got %q", got)
	}
}

func duckDBTestHasUniqueIndexColumn(indexes []connection.IndexDefinition, columnName string) bool {
	for _, index := range indexes {
		if index.ColumnName == columnName && index.NonUnique == 0 {
			return true
		}
	}
	return false
}
