//go:build integration
// +build integration

package integration_test

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

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

func TestDMLLiteralBooleanBlob(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 := "literal_items"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int PRIMARY KEY, is_active boolean, payload blob)",
		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, is_active, payload) VALUES (1, true, 0xdeadbeef)",
		keyspace,
		table,
	)
	if err := session.Query(insert).Exec(); err != nil {
		t.Fatalf("insert failed: %v", err)
	}

	var (
		active  bool
		payload []byte
	)
	selectRow := fmt.Sprintf("SELECT is_active, payload FROM %s.%s WHERE id = 1", keyspace, table)
	if err := session.Query(selectRow).Scan(&active, &payload); err != nil {
		t.Fatalf("select failed: %v", err)
	}
	if !active {
		t.Fatalf("expected is_active to be true")
	}
	if !bytes.Equal(payload, []byte{0xde, 0xad, 0xbe, 0xef}) {
		t.Fatalf("unexpected payload: %v", payload)
	}
}

func TestDMLLiteralEmptyBlob(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 := "literal_empty_blob"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int PRIMARY KEY, payload blob)",
		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, payload) VALUES (1, 0x)",
		keyspace,
		table,
	)
	if err := session.Query(insert).Exec(); err != nil {
		t.Fatalf("insert failed: %v", err)
	}

	client := newRawProtocolConn(t, cql.ProtoVersion4, rawProtocolOptions{})
	payload := readSingleBlobValue(t, client, fmt.Sprintf("SELECT payload FROM %s.%s WHERE id = 1", keyspace, table), 4)
	if payload == nil || len(payload) != 0 {
		t.Fatalf("expected empty blob, got %v", payload)
	}
}

func TestDMLLiteralTimestampWithTimezone(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 := "literal_timestamp_tz"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int PRIMARY KEY, ts timestamp)",
		keyspace,
		table,
	)
	if err := session.Query(createTable).Exec(); err != nil {
		t.Fatalf("create table failed: %v", err)
	}

	insertOne := fmt.Sprintf(
		"INSERT INTO %s.%s (id, ts) VALUES (1, '1970-01-01 00:00:00+0000')",
		keyspace,
		table,
	)
	if err := session.Query(insertOne).Exec(); err != nil {
		t.Fatalf("insert row1 failed: %v", err)
	}

	insertTwo := fmt.Sprintf(
		"INSERT INTO %s.%s (id, ts) VALUES (2, '2050-12-31 23:59:59+0000')",
		keyspace,
		table,
	)
	if err := session.Query(insertTwo).Exec(); err != nil {
		t.Fatalf("insert row2 failed: %v", err)
	}

	var ts time.Time
	selectOne := fmt.Sprintf("SELECT ts FROM %s.%s WHERE id = 1", keyspace, table)
	if err := session.Query(selectOne).Scan(&ts); err != nil {
		t.Fatalf("select row1 failed: %v", err)
	}
	if got := ts.UTC().Format("2006-01-02 15:04:05.000"); got != "1970-01-01 00:00:00.000" {
		t.Fatalf("unexpected row1 timestamp: %s", got)
	}

	selectTwo := fmt.Sprintf("SELECT ts FROM %s.%s WHERE id = 2", keyspace, table)
	if err := session.Query(selectTwo).Scan(&ts); err != nil {
		t.Fatalf("select row2 failed: %v", err)
	}
	if got := ts.UTC().Format("2006-01-02 15:04:05.000"); got != "2050-12-31 23:59:59.000" {
		t.Fatalf("unexpected row2 timestamp: %s", got)
	}
}

func TestDMLLiteralStringBackslash(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 := "literal_backslash"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int PRIMARY KEY, val 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, val) VALUES (1, 'Value with \\ backslash')",
		keyspace,
		table,
	)
	if err := session.Query(insert).Exec(); err != nil {
		t.Fatalf("insert failed: %v", err)
	}

	var val string
	selectRow := fmt.Sprintf("SELECT val FROM %s.%s WHERE id = 1", keyspace, table)
	if err := session.Query(selectRow).Scan(&val); err != nil {
		t.Fatalf("select failed: %v", err)
	}
	if val != "Value with \\ backslash" {
		t.Fatalf("unexpected val: %q", val)
	}
}
