package db

import (
	"strings"
	"testing"
)

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

	query := buildDuckDBConstraintMetadataQuery(duckDBObjectPath{
		Catalog: "analytics",
		Schema:  "main",
		Object:  "events",
	}, true)

	if !containsAll(query,
		"FROM duckdb_constraints()",
		"constraint_type IN ('PRIMARY KEY', 'UNIQUE')",
		"database_name = 'analytics'",
		"schema_name = 'main'",
		"table_name = 'events'",
	) {
		t.Fatalf("DuckDB 约束查询未正确包含 catalog/schema/table 过滤: %s", query)
	}
}

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

	query := buildDuckDBIndexMetadataQuery(duckDBObjectPath{
		Catalog: "analytics",
		Schema:  "main",
		Object:  "events",
	}, true)

	if !containsAll(query,
		"FROM duckdb_indexes()",
		"database_name = 'analytics'",
		"schema_name = 'main'",
		"table_name = 'events'",
	) {
		t.Fatalf("DuckDB 索引查询未正确包含 catalog/schema/table 过滤: %s", query)
	}
}

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

	columns := buildDuckDBColumnDefinitions(
		[]map[string]interface{}{
			{
				"column_name":    "id",
				"data_type":      "BIGINT",
				"is_nullable":    "NO",
				"column_default": nil,
			},
			{
				"column_name":    "email",
				"data_type":      "VARCHAR",
				"is_nullable":    "YES",
				"column_default": "'guest@example.com'",
			},
		},
		[]map[string]interface{}{
			{
				"constraint_name":         "events_pkey",
				"constraint_type":         "PRIMARY KEY",
				"constraint_column_names": "[id]",
			},
			{
				"constraint_name":         "events_email_key",
				"constraint_type":         "UNIQUE",
				"constraint_column_names": "[email]",
			},
		},
	)

	if len(columns) != 2 {
		t.Fatalf("unexpected column count: %d", len(columns))
	}
	if columns[0].Name != "id" || columns[0].Key != "PRI" {
		t.Fatalf("主键列未正确标记: %+v", columns[0])
	}
	if columns[1].Name != "email" || columns[1].Key != "UNI" {
		t.Fatalf("唯一键列未正确标记: %+v", columns[1])
	}
	if columns[1].Default == nil || *columns[1].Default != "'guest@example.com'" {
		t.Fatalf("默认值未保留: %+v", columns[1])
	}
}

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

	columns := buildDuckDBColumnDefinitions(
		[]map[string]interface{}{
			{
				"column_name": "id",
				"data_type":   "BIGINT",
				"is_nullable": "NO",
			},
			{
				"column_name": "tenant_id",
				"data_type":   "BIGINT",
				"is_nullable": "NO",
			},
			{
				"column_name": "slug",
				"data_type":   "VARCHAR",
				"is_nullable": "NO",
			},
		},
		[]map[string]interface{}{
			{
				"constraint_name":         "events_pkey",
				"constraint_type":         "PRIMARY KEY",
				"constraint_column_names": []interface{}{"id", "tenant_id"},
			},
			{
				"constraint_name":         "events_slug_key",
				"constraint_type":         "UNIQUE",
				"constraint_column_names": []string{"slug"},
			},
		},
	)

	if len(columns) != 3 {
		t.Fatalf("unexpected column count: %d", len(columns))
	}
	if columns[0].Key != "PRI" || columns[1].Key != "PRI" {
		t.Fatalf("复合主键列未正确标记: %+v", columns)
	}
	if columns[2].Key != "UNI" {
		t.Fatalf("唯一键列未正确标记: %+v", columns[2])
	}
}

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

	indexes := buildDuckDBIndexDefinitions(
		[]map[string]interface{}{
			{
				"constraint_name":         "events_pkey",
				"constraint_type":         "PRIMARY KEY",
				"constraint_column_names": "[id]",
			},
			{
				"constraint_name":         "events_business_key",
				"constraint_type":         "UNIQUE",
				"constraint_column_names": "[email, region]",
			},
		},
		[]map[string]interface{}{
			{
				"index_name":  "idx_events_slug",
				"is_unique":   true,
				"expressions": "[slug]",
			},
		},
	)

	if len(indexes) != 4 {
		t.Fatalf("unexpected index row count: %d", len(indexes))
	}
	if indexes[0].Name != "events_pkey" || indexes[0].ColumnName != "id" || indexes[0].NonUnique != 0 {
		t.Fatalf("主键索引映射异常: %+v", indexes[0])
	}
	if indexes[1].Name != "events_business_key" || indexes[1].ColumnName != "email" || indexes[1].SeqInIndex != 1 {
		t.Fatalf("约束唯一索引首列映射异常: %+v", indexes[1])
	}
	if indexes[2].Name != "events_business_key" || indexes[2].ColumnName != "region" || indexes[2].SeqInIndex != 2 {
		t.Fatalf("约束唯一索引次列映射异常: %+v", indexes[2])
	}
	if indexes[3].Name != "idx_events_slug" || indexes[3].ColumnName != "slug" || indexes[3].NonUnique != 0 || indexes[3].IndexType != "INDEX" {
		t.Fatalf("显式唯一索引映射异常: %+v", indexes[3])
	}
}

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

	indexes := buildDuckDBIndexDefinitions(
		[]map[string]interface{}{
			{
				"constraint_name":         "events_business_key",
				"constraint_type":         "UNIQUE",
				"constraint_column_names": []interface{}{"tenant_id", "slug"},
			},
		},
		[]map[string]interface{}{
			{
				"index_name":  "idx_events_expr",
				"is_unique":   true,
				"expressions": []string{`lower("slug")`},
			},
		},
	)

	if len(indexes) != 3 {
		t.Fatalf("unexpected index row count: %d", len(indexes))
	}
	if indexes[0].Name != "events_business_key" || indexes[0].ColumnName != "tenant_id" || indexes[0].SeqInIndex != 1 {
		t.Fatalf("约束唯一索引首列映射异常: %+v", indexes[0])
	}
	if indexes[1].Name != "events_business_key" || indexes[1].ColumnName != "slug" || indexes[1].SeqInIndex != 2 {
		t.Fatalf("约束唯一索引次列映射异常: %+v", indexes[1])
	}
	if indexes[2].Name != "idx_events_expr" || indexes[2].ColumnName != `lower("slug")` || indexes[2].NonUnique != 0 {
		t.Fatalf("表达式唯一索引映射异常: %+v", indexes[2])
	}
}

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

	path := normalizeDuckDBObjectPath(`"analytics.catalog"."main.schema"`, `"daily.events"."2026.06"`)
	if path.Catalog != "analytics.catalog" || path.Schema != "daily.events" || path.Object != "2026.06" {
		t.Fatalf("unexpected duckdb path: %+v", path)
	}

	qualified := normalizeDuckDBObjectPath(`analytics`, `"main.schema"."daily.events"`)
	if qualified.Catalog != "analytics" || qualified.Schema != "main.schema" || qualified.Object != "daily.events" {
		t.Fatalf("unexpected duckdb qualified path without catalog: %+v", qualified)
	}
}

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

	path := normalizeDuckDBObjectPath("main", "main.events")
	if path.Catalog != "" || path.Schema != "main" || path.Object != "events" {
		t.Fatalf("unexpected duckdb main path: %+v", path)
	}

	memoryPath := normalizeDuckDBObjectPath("memory", "main.events")
	if memoryPath.Catalog != "" || memoryPath.Schema != "main" || memoryPath.Object != "events" {
		t.Fatalf("unexpected duckdb memory path: %+v", memoryPath)
	}
}

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

	parts := parseDuckDBExpressionList(`["slug", lower("name.with.dot")]`)
	if len(parts) != 2 || parts[0] != `slug` || parts[1] != `lower("name.with.dot")` {
		t.Fatalf("unexpected expression list: %#v", parts)
	}
}

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

	parts := parseDuckDBExpressionList(`['"name"', '"tenant.id"', 'slug']`)
	if len(parts) != 3 || parts[0] != "name" || parts[1] != "tenant.id" || parts[2] != "slug" {
		t.Fatalf("unexpected expression list: %#v", parts)
	}

	exprParts := parseDuckDBExpressionList(`['lower("name")']`)
	if len(exprParts) != 1 || exprParts[0] != `'lower("name")'` {
		t.Fatalf("expression literal should be preserved: %#v", exprParts)
	}
}

func containsAll(source string, needles ...string) bool {
	for _, needle := range needles {
		if !strings.Contains(source, needle) {
			return false
		}
	}
	return true
}
