package systemtables

import (
	"context"
	"testing"

	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/parser"
	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/types"
)

type stubSchemaProvider struct {
	typesRows      []types.Row
	viewsRows      []types.Row
	triggersRows   []types.Row
	functionsRows  []types.Row
	aggregatesRows []types.Row
}

func (s *stubSchemaProvider) Keyspaces(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return nil, nil
}

func (s *stubSchemaProvider) Tables(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return nil, nil
}

func (s *stubSchemaProvider) Columns(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return nil, nil
}

func (s *stubSchemaProvider) Indexes(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return nil, nil
}

func (s *stubSchemaProvider) Types(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return s.typesRows, nil
}

func (s *stubSchemaProvider) Views(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return s.viewsRows, nil
}

func (s *stubSchemaProvider) Triggers(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return s.triggersRows, nil
}

func (s *stubSchemaProvider) Functions(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return s.functionsRows, nil
}

func (s *stubSchemaProvider) Aggregates(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return s.aggregatesRows, nil
}

func (s *stubSchemaProvider) Users(ctx context.Context, columns []types.ColumnInfo, user, password string) ([]types.Row, error) {
	return nil, nil
}

func TestSchemaProviderAddsTypesViewsTriggersFunctionsAggregates(t *testing.T) {
	registry := NewRegistry()
	executor := NewSystemTableExecutor(registry)

	typeTable, _ := registry.Lookup("system_schema.types")
	viewTable, _ := registry.Lookup("system_schema.views")
	triggerTable, _ := registry.Lookup("system_schema.triggers")
	functionTable, _ := registry.Lookup("system_schema.functions")
	aggregateTable, _ := registry.Lookup("system_schema.aggregates")

	provider := &stubSchemaProvider{
		typesRows: []types.Row{
			buildRow(typeTable.Columns, map[string]interface{}{
				"keyspace_name": "ks",
				"type_name":     "addr",
				"field_names":   []string{"street"},
				"field_types":   []string{"text"},
			}),
		},
		viewsRows: []types.Row{
			buildRow(viewTable.Columns, map[string]interface{}{
				"keyspace_name":   "ks",
				"view_name":       "mv",
				"base_table_name": "tbl",
				"where_clause":    "id IS NOT NULL",
				"base_table_id":   mustTableID(t, "ks", "tbl"),
				"id":              mustViewID(t, "ks", "mv"),
			}),
		},
		triggersRows: []types.Row{
			buildRow(triggerTable.Columns, map[string]interface{}{
				"keyspace_name": "ks",
				"table_name":    "tbl",
				"trigger_name":  "tr1",
				"options":       map[string]string{"class": "com.example.Trigger"},
			}),
		},
		functionsRows: []types.Row{
			buildRow(functionTable.Columns, map[string]interface{}{
				"keyspace_name":        "ks",
				"function_name":        "fn1",
				"argument_types":       []string{"text"},
				"argument_names":       []string{"value"},
				"body":                 "return value",
				"language":             "java",
				"return_type":          "text",
				"called_on_null_input": true,
			}),
		},
		aggregatesRows: []types.Row{
			buildRow(aggregateTable.Columns, map[string]interface{}{
				"keyspace_name":  "ks",
				"aggregate_name": "agg1",
				"argument_types": []string{"int"},
				"final_func":     "fn1",
				"initcond":       "0",
				"return_type":    "int",
				"state_func":     "fn1",
				"state_type":     "int",
			}),
		},
	}

	executor.SetSchemaProvider(provider)

	tests := []struct {
		name      string
		query     string
		wantRows  int
		wantTable string
	}{
		{name: "types", query: "SELECT * FROM system_schema.types", wantRows: 1, wantTable: "system_schema.types"},
		{name: "views", query: "SELECT * FROM system_schema.views", wantRows: 1, wantTable: "system_schema.views"},
		{name: "triggers", query: "SELECT * FROM system_schema.triggers", wantRows: 1, wantTable: "system_schema.triggers"},
		{name: "functions", query: "SELECT * FROM system_schema.functions", wantRows: 1, wantTable: "system_schema.functions"},
		{name: "aggregates", query: "SELECT * FROM system_schema.aggregates", wantRows: 1, wantTable: "system_schema.aggregates"},
	}

	ctx := context.Background()
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			stmt, err := parser.NewParser(tt.query).Parse()
			if err != nil {
				t.Fatalf("parse failed: %v", err)
			}
			selectStmt := stmt.(*parser.SelectStatement)
			result, err := executor.Execute(ctx, selectStmt, "user", "pass")
			if err != nil {
				t.Fatalf("execute failed: %v", err)
			}
			if len(result.Rows) != tt.wantRows {
				t.Fatalf("expected %d rows, got %d", tt.wantRows, len(result.Rows))
			}
		})
	}
}

func mustTableID(t *testing.T, keyspace, table string) string {
	t.Helper()
	id, err := tableIDFor(keyspace, table)
	if err != nil {
		t.Fatalf("tableIDFor failed: %v", err)
	}
	return id
}

func mustViewID(t *testing.T, keyspace, view string) string {
	t.Helper()
	id, err := viewIDFor(keyspace, view)
	if err != nil {
		t.Fatalf("viewIDFor failed: %v", err)
	}
	return id
}
