package executor

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"go.uber.org/zap"

	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/systemtables"
	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/virtualtables"
)

func TestDescribeKeyspaces(t *testing.T) {
	executor := &Executor{
		logger:         zap.NewNop(),
		systemExecutor: systemtables.NewSystemTableExecutor(systemtables.NewRegistry()),
	}

	result, err := executor.Execute(context.Background(), "test_user", "test_password", "DESC KEYSPACES")
	if err != nil {
		t.Fatalf("DESC KEYSPACES failed: %v", err)
	}
	if !result.UseGlobalSpec {
		t.Fatalf("expected global spec metadata for DESCRIBE KEYSPACES")
	}
	if len(result.Columns) != 3 {
		t.Fatalf("expected 3 columns, got %+v", result.Columns)
	}
	expected := []string{"keyspace_name", "type", "name"}
	for i, col := range result.Columns {
		if col.Keyspace != "system" || col.Table != "describe" {
			t.Fatalf("expected system.describe metadata, got %s.%s", col.Keyspace, col.Table)
		}
		if col.Name != expected[i] {
			t.Fatalf("expected column %q at %d, got %q", expected[i], i, col.Name)
		}
	}
	if len(result.Rows) == 0 {
		t.Fatal("expected at least one keyspace row")
	}
}

func TestDescribeTable(t *testing.T) {
	executor := &Executor{
		logger:         zap.NewNop(),
		systemExecutor: systemtables.NewSystemTableExecutor(systemtables.NewRegistry()),
	}

	result, err := executor.Execute(context.Background(), "test_user", "test_password", "DESC TABLE system.local")
	if err != nil {
		t.Fatalf("DESC TABLE failed: %v", err)
	}
	if !result.UseGlobalSpec {
		t.Fatalf("expected global spec metadata for DESCRIBE TABLE")
	}
	if len(result.Columns) != 4 {
		t.Fatalf("expected 4 columns, got %+v", result.Columns)
	}
	expected := []string{"keyspace_name", "type", "name", "create_statement"}
	for i, col := range result.Columns {
		if col.Keyspace != "system" || col.Table != "describe" {
			t.Fatalf("expected system.describe metadata, got %s.%s", col.Keyspace, col.Table)
		}
		if col.Name != expected[i] {
			t.Fatalf("expected column %q at %d, got %q", expected[i], i, col.Name)
		}
	}
	if len(result.Rows) != 1 {
		t.Fatalf("expected one row for system.local, got %d", len(result.Rows))
	}
	row := result.Rows[0]
	if len(row.Values) != 4 {
		t.Fatalf("expected 4 row values, got %d", len(row.Values))
	}
	if row.Values[0] != "system" || row.Values[1] != "table" || row.Values[2] != "local" {
		t.Fatalf("unexpected describe row values: %+v", row.Values[:3])
	}
	createStatement := ""
	if row.Values[3] != nil {
		createStatement = row.Values[3].(string)
	}
	if createStatement == "" {
		t.Fatalf("expected create_statement for system.local")
	}
	if !strings.Contains(createStatement, "CREATE TABLE system.local") {
		t.Fatalf("expected create statement for system.local, got %q", createStatement)
	}
	if !strings.Contains(createStatement, "PRIMARY KEY (key)") {
		t.Fatalf("expected primary key for system.local, got %q", createStatement)
	}
}

func TestDescribeKeyspace(t *testing.T) {
	executor := &Executor{
		logger:         zap.NewNop(),
		systemExecutor: systemtables.NewSystemTableExecutor(systemtables.NewRegistry()),
	}

	result, err := executor.Execute(context.Background(), "test_user", "test_password", "DESCRIBE KEYSPACE system")
	if err != nil {
		t.Fatalf("DESCRIBE KEYSPACE failed: %v", err)
	}
	if !result.UseGlobalSpec {
		t.Fatalf("expected global spec metadata for DESCRIBE KEYSPACE")
	}
	if len(result.Columns) != 4 {
		t.Fatalf("expected 4 columns, got %+v", result.Columns)
	}
	expected := []string{"keyspace_name", "type", "name", "create_statement"}
	for i, col := range result.Columns {
		if col.Keyspace != "system" || col.Table != "describe" {
			t.Fatalf("expected system.describe metadata, got %s.%s", col.Keyspace, col.Table)
		}
		if col.Name != expected[i] {
			t.Fatalf("expected column %q at %d, got %q", expected[i], i, col.Name)
		}
	}
	if len(result.Rows) == 0 {
		t.Fatal("expected at least one row for DESCRIBE KEYSPACE")
	}
	row := result.Rows[0]
	if len(row.Values) != 4 {
		t.Fatalf("expected 4 row values, got %d", len(row.Values))
	}
	if fmt.Sprint(row.Values[0]) != "system" || fmt.Sprint(row.Values[1]) != "keyspace" || fmt.Sprint(row.Values[2]) != "system" {
		t.Fatalf("unexpected keyspace row values: %+v", row.Values[:3])
	}
	createStatement := fmt.Sprint(row.Values[3])
	if !strings.Contains(createStatement, "CREATE KEYSPACE system") {
		t.Fatalf("expected keyspace create statement, got %q", createStatement)
	}
	if !strings.Contains(createStatement, "LocalStrategy") {
		t.Fatalf("expected LocalStrategy replication, got %q", createStatement)
	}
	if !strings.Contains(createStatement, "durable_writes = true") {
		t.Fatalf("expected durable_writes clause, got %q", createStatement)
	}

	foundTable := false
	for _, row := range result.Rows[1:] {
		if len(row.Values) < 4 {
			continue
		}
		if fmt.Sprint(row.Values[1]) != "table" {
			continue
		}
		foundTable = true
		tableStmt := fmt.Sprint(row.Values[3])
		if tableStmt == "" {
			t.Fatalf("expected create_statement for table row")
		}
		if !strings.Contains(tableStmt, "CREATE TABLE system.") {
			t.Fatalf("expected table create statement, got %q", tableStmt)
		}
		break
	}
	if !foundTable {
		t.Fatalf("expected at least one table row for DESCRIBE KEYSPACE")
	}
}

func TestDescribeTableClusteringOrder(t *testing.T) {
	executor := &Executor{
		logger:          zap.NewNop(),
		systemExecutor:  systemtables.NewSystemTableExecutor(systemtables.NewRegistry()),
		virtualExecutor: virtualtables.NewVirtualTableExecutor(virtualtables.NewRegistry()),
	}

	result, err := executor.Execute(context.Background(), "test_user", "test_password", "DESC TABLE system.consensus_migration_state")
	if err != nil {
		t.Fatalf("DESC TABLE failed: %v", err)
	}
	if len(result.Rows) != 1 {
		t.Fatalf("expected one row, got %d", len(result.Rows))
	}
	createStatement := ""
	if result.Rows[0].Values[3] != nil {
		createStatement = result.Rows[0].Values[3].(string)
	}
	if !strings.Contains(createStatement, "WITH CLUSTERING ORDER BY (cf_id ASC, consensus_migrated_at_epoch DESC)") {
		t.Fatalf("expected clustering order clause, got %q", createStatement)
	}
}

func TestDescribeTableCompositePartitionKey(t *testing.T) {
	executor := &Executor{
		logger:          zap.NewNop(),
		systemExecutor:  systemtables.NewSystemTableExecutor(systemtables.NewRegistry()),
		virtualExecutor: virtualtables.NewVirtualTableExecutor(virtualtables.NewRegistry()),
	}

	result, err := executor.Execute(context.Background(), "test_user", "test_password", "DESC TABLE system.sstable_activity_v2")
	if err != nil {
		t.Fatalf("DESC TABLE failed: %v", err)
	}
	if len(result.Rows) != 1 {
		t.Fatalf("expected one row, got %d", len(result.Rows))
	}
	createStatement := ""
	if result.Rows[0].Values[3] != nil {
		createStatement = result.Rows[0].Values[3].(string)
	}
	if !strings.Contains(createStatement, "PRIMARY KEY ((keyspace_name, table_name, id))") {
		t.Fatalf("expected composite partition key, got %q", createStatement)
	}
}

func TestDescribeTables(t *testing.T) {
	executor := &Executor{
		logger:         zap.NewNop(),
		systemExecutor: systemtables.NewSystemTableExecutor(systemtables.NewRegistry()),
	}

	result, err := executor.Execute(context.Background(), "test_user", "test_password", "DESCRIBE TABLES")
	if err != nil {
		t.Fatalf("DESCRIBE TABLES failed: %v", err)
	}
	if !result.UseGlobalSpec {
		t.Fatalf("expected global spec metadata for DESCRIBE TABLES")
	}
	if len(result.Columns) != 3 {
		t.Fatalf("expected 3 columns, got %+v", result.Columns)
	}
	expected := []string{"keyspace_name", "type", "name"}
	for i, col := range result.Columns {
		if col.Keyspace != "system" || col.Table != "describe" {
			t.Fatalf("expected system.describe metadata, got %s.%s", col.Keyspace, col.Table)
		}
		if col.Name != expected[i] {
			t.Fatalf("expected column %q at %d, got %q", expected[i], i, col.Name)
		}
	}
	if len(result.Rows) == 0 {
		t.Fatal("expected at least one table row")
	}
}
