package executor

import (
	"errors"
	"testing"

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

func TestFilterInsertStatementUnset(t *testing.T) {
	stmt, err := parser.NewParser("INSERT INTO ks.tbl (pk, v1, v2) VALUES (?, ?, ?)").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	insertStmt := stmt.(*parser.InsertStatement)
	markerIndex := buildBindMarkerIndex(insertStmt)
	params := []interface{}{1, UnsetValue, "x"}

	filtered, remapped, changed, noOp, err := filterInsertStatementForUnset(insertStmt, params, markerIndex, []string{"pk"})
	if err != nil {
		t.Fatalf("filterInsertStatementForUnset failed: %v", err)
	}
	if noOp {
		t.Fatalf("expected noOp=false")
	}
	if !changed {
		t.Fatalf("expected changed=true")
	}
	if len(filtered.Columns) != 2 || filtered.Columns[0] != "pk" || filtered.Columns[1] != "v2" {
		t.Fatalf("unexpected filtered columns: %#v", filtered.Columns)
	}
	if len(remapped) != 2 || remapped[0] != params[0] || remapped[1] != params[2] {
		t.Fatalf("unexpected remapped params: %#v", remapped)
	}
}

func TestFilterInsertStatementUnsetPrimaryKey(t *testing.T) {
	stmt, err := parser.NewParser("INSERT INTO ks.tbl (pk, v1) VALUES (?, ?)").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	insertStmt := stmt.(*parser.InsertStatement)
	markerIndex := buildBindMarkerIndex(insertStmt)
	params := []interface{}{UnsetValue, 1}

	_, _, _, _, err = filterInsertStatementForUnset(insertStmt, params, markerIndex, []string{"pk"})
	if err == nil {
		t.Fatalf("expected error for unset primary key")
	}
	var invalidErr *cqlerrors.Error
	if !errors.As(err, &invalidErr) {
		t.Fatalf("expected cqlerrors.Invalid, got %v", err)
	}
}

func TestFilterInsertStatementUnsetMixedNull(t *testing.T) {
	stmt, err := parser.NewParser("INSERT INTO ks.tbl (pk, v1, v2) VALUES (?, ?, ?)").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	insertStmt := stmt.(*parser.InsertStatement)
	markerIndex := buildBindMarkerIndex(insertStmt)
	params := []interface{}{1, nil, UnsetValue}

	filtered, remapped, changed, noOp, err := filterInsertStatementForUnset(insertStmt, params, markerIndex, []string{"pk"})
	if err != nil {
		t.Fatalf("filterInsertStatementForUnset failed: %v", err)
	}
	if noOp {
		t.Fatalf("expected noOp=false")
	}
	if !changed {
		t.Fatalf("expected changed=true")
	}
	if len(filtered.Columns) != 2 || filtered.Columns[0] != "pk" || filtered.Columns[1] != "v1" {
		t.Fatalf("unexpected filtered columns: %#v", filtered.Columns)
	}
	if len(remapped) != 2 || remapped[0] != params[0] || remapped[1] != params[1] {
		t.Fatalf("unexpected remapped params: %#v", remapped)
	}
}

func TestFilterInsertStatementUnsetMissingPKNoOp(t *testing.T) {
	stmt, err := parser.NewParser("INSERT INTO ks.tbl (v1) VALUES (?)").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	insertStmt := stmt.(*parser.InsertStatement)
	markerIndex := buildBindMarkerIndex(insertStmt)
	params := []interface{}{UnsetValue}

	_, _, changed, noOp, err := filterInsertStatementForUnset(insertStmt, params, markerIndex, []string{"pk"})
	if err != nil {
		t.Fatalf("filterInsertStatementForUnset failed: %v", err)
	}
	if !changed {
		t.Fatalf("expected changed=true")
	}
	if !noOp {
		t.Fatalf("expected noOp=true")
	}
}

func TestFilterUpdateStatementUnsetNoOp(t *testing.T) {
	stmt, err := parser.NewParser("UPDATE ks.tbl SET v1 = ?, v2 = ? WHERE pk = ?").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	updateStmt := stmt.(*parser.UpdateStatement)
	markerIndex := buildBindMarkerIndex(updateStmt)
	params := []interface{}{UnsetValue, UnsetValue, 1}

	_, _, changed, noOp, err := filterUpdateStatementForUnset(updateStmt, params, markerIndex)
	if err != nil {
		t.Fatalf("filterUpdateStatementForUnset failed: %v", err)
	}
	if !changed {
		t.Fatalf("expected changed=true")
	}
	if !noOp {
		t.Fatalf("expected noOp=true")
	}
}

func TestFilterUpdateStatementUnsetDropAssignment(t *testing.T) {
	stmt, err := parser.NewParser("UPDATE ks.tbl SET v1 = ?, v2 = ? WHERE pk = ?").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	updateStmt := stmt.(*parser.UpdateStatement)
	markerIndex := buildBindMarkerIndex(updateStmt)
	params := []interface{}{1, UnsetValue, 2}

	filtered, remapped, changed, noOp, err := filterUpdateStatementForUnset(updateStmt, params, markerIndex)
	if err != nil {
		t.Fatalf("filterUpdateStatementForUnset failed: %v", err)
	}
	if noOp {
		t.Fatalf("expected noOp=false")
	}
	if !changed {
		t.Fatalf("expected changed=true")
	}
	if len(filtered.Assigments) != 1 || filtered.Assigments[0].Column != "v1" {
		t.Fatalf("unexpected filtered assignments: %#v", filtered.Assigments)
	}
	if len(remapped) != 2 || remapped[0] != params[0] || remapped[1] != params[2] {
		t.Fatalf("unexpected remapped params: %#v", remapped)
	}
}

func TestFilterUpdateStatementUnsetRejectWhere(t *testing.T) {
	stmt, err := parser.NewParser("UPDATE ks.tbl SET v1 = ? WHERE pk = ?").Parse()
	if err != nil {
		t.Fatalf("parse failed: %v", err)
	}
	updateStmt := stmt.(*parser.UpdateStatement)
	markerIndex := buildBindMarkerIndex(updateStmt)
	params := []interface{}{1, UnsetValue}

	_, _, _, _, err = filterUpdateStatementForUnset(updateStmt, params, markerIndex)
	if err == nil {
		t.Fatalf("expected error for unset where clause")
	}
	var invalidErr *cqlerrors.Error
	if !errors.As(err, &invalidErr) {
		t.Fatalf("expected cqlerrors.Invalid, got %v", err)
	}
}
