//go:build integration
// +build integration

package integration_test

import (
	"bytes"
	"fmt"
	"testing"
	"time"
)

func TestDMLPreparedCRUD(t *testing.T) {
	session := newSession(t, sessionOptions{disableHostLookup: true})

	keyspace := uniqueKeyspace()
	if err := session.Query(createKeyspaceCQL(keyspace)).Exec(); err != nil {
		t.Fatalf("create keyspace failed: %v", err)
	}
	t.Cleanup(func() {
		_ = session.Query(dropKeyspaceCQL(keyspace)).Exec()
	})

	table := "dml_items"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int, name text, idx int, active boolean, score double, created timestamp, payload blob, PRIMARY KEY (id))",
		keyspace,
		table,
	)
	if err := session.Query(createTable).Exec(); err != nil {
		t.Fatalf("create table failed: %v", err)
	}

	insert := fmt.Sprintf(
		"INSERT INTO %s.%s (id, name, idx, active, score, created, payload) VALUES (?, ?, ?, ?, ?, ?, ?)",
		keyspace,
		table,
	)

	ts1 := time.Now().UTC().Truncate(time.Millisecond)
	ts2 := ts1.Add(2 * time.Minute).Truncate(time.Millisecond)
	ts3 := ts2.Add(2 * time.Minute).Truncate(time.Millisecond)

	if err := session.Query(insert, 1, "alpha", 1, true, 1.5, ts1, []byte("blob1")).Exec(); err != nil {
		t.Fatalf("insert row1 failed: %v", err)
	}
	if err := session.Query(insert, 2, "beta", 3, false, 2.5, ts2, []byte("blob2")).Exec(); err != nil {
		t.Fatalf("insert row2 failed: %v", err)
	}
	if err := session.Query(insert, 3, "beta", 2, true, 3.5, ts3, []byte("blob3")).Exec(); err != nil {
		t.Fatalf("insert row3 failed: %v", err)
	}

	update := fmt.Sprintf("UPDATE %s.%s SET name = ?, idx = ? WHERE id = ?", keyspace, table)
	if err := session.Query(update, "gamma", 4, 2).Exec(); err != nil {
		t.Fatalf("update row2 failed: %v", err)
	}

	var (
		name    string
		idx     int
		active  bool
		score   float64
		created time.Time
		payload []byte
	)
	selectRow := fmt.Sprintf("SELECT name, idx, active, score, created, payload FROM %s.%s WHERE id = ?", keyspace, table)
	if err := session.Query(selectRow, 2).Scan(&name, &idx, &active, &score, &created, &payload); err != nil {
		t.Fatalf("select row2 failed: %v", err)
	}
	if name != "gamma" || idx != 4 || active {
		t.Fatalf("unexpected row2 values: name=%q idx=%d active=%t", name, idx, active)
	}
	if score != 2.5 {
		t.Fatalf("unexpected score: %v", score)
	}
	if created.UnixMilli() != ts2.UnixMilli() {
		t.Fatalf("unexpected created timestamp: %v", created)
	}
	if !bytes.Equal(payload, []byte("blob2")) {
		t.Fatalf("unexpected payload: %v", payload)
	}

	orderQuery := fmt.Sprintf("SELECT id, idx FROM %s.%s ORDER BY idx DESC LIMIT 1", keyspace, table)
	rows, err := scanRows(session, orderQuery)
	if err != nil {
		t.Fatalf("order by query failed: %v", err)
	}
	if len(rows) != 1 {
		t.Fatalf("expected 1 row, got %d", len(rows))
	}
	if asString(rows[0]["id"]) != "2" {
		t.Fatalf("expected id 2 as max idx, got %v", rows[0]["id"])
	}

	distinctQuery := fmt.Sprintf("SELECT DISTINCT name FROM %s.%s", keyspace, table)
	rows, err = scanRows(session, distinctQuery)
	if err != nil {
		t.Fatalf("distinct query failed: %v", err)
	}
	if len(rows) != 3 {
		t.Fatalf("expected 3 distinct names, got %d", len(rows))
	}

	deleteQuery := fmt.Sprintf("DELETE FROM %s.%s WHERE id = ?", keyspace, table)
	if err := session.Query(deleteQuery, 1).Exec(); err != nil {
		t.Fatalf("delete row1 failed: %v", err)
	}

	rows, err = scanRows(session, fmt.Sprintf("SELECT id FROM %s.%s WHERE id = 1", keyspace, table))
	if err != nil {
		t.Fatalf("select deleted row failed: %v", err)
	}
	if len(rows) != 0 {
		t.Fatalf("expected row1 to be deleted")
	}
}

func TestSelectOffset(t *testing.T) {
	session := newSession(t, sessionOptions{disableHostLookup: true})

	keyspace := uniqueKeyspace()
	if err := session.Query(createKeyspaceCQL(keyspace)).Exec(); err != nil {
		t.Fatalf("create keyspace failed: %v", err)
	}
	t.Cleanup(func() {
		_ = session.Query(dropKeyspaceCQL(keyspace)).Exec()
	})

	table := "offset_items"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int PRIMARY KEY, name text)",
		keyspace,
		table,
	)
	if err := session.Query(createTable).Exec(); err != nil {
		t.Fatalf("create table failed: %v", err)
	}

	insert := fmt.Sprintf("INSERT INTO %s.%s (id, name) VALUES (?, ?)", keyspace, table)
	for i := 1; i <= 4; i++ {
		if err := session.Query(insert, i, fmt.Sprintf("n%d", i)).Exec(); err != nil {
			t.Fatalf("insert row%d failed: %v", i, err)
		}
	}

	query := fmt.Sprintf("SELECT id FROM %s.%s WHERE id >= 1 ORDER BY id LIMIT 2 OFFSET 1", keyspace, table)
	rows, err := scanRows(session, query)
	if err != nil {
		t.Fatalf("offset query failed: %v", err)
	}
	if len(rows) != 2 {
		t.Fatalf("expected 2 rows, got %d", len(rows))
	}
	if asString(rows[0]["id"]) != "2" || asString(rows[1]["id"]) != "3" {
		t.Fatalf("unexpected offset results: %+v", rows)
	}
}
