package sqlite

import (
	"bytes"
	"strings"
	"testing"

	"git.warky.dev/wdevs/relspecgo/pkg/models"
	"git.warky.dev/wdevs/relspecgo/pkg/writers"
)

func sqliteDirectiveDB(t *testing.T) *models.Database {
	t.Helper()
	db := models.InitDatabase("testdb")
	schema := models.InitSchema("public")
	table := models.InitTable("events", "public")

	id := models.InitColumn("id", "events", "public")
	id.Type = "bigint"
	id.IsPrimaryKey = true
	id.NotNull = true
	table.Columns["id"] = id

	name := models.InitColumn("name", "events", "public")
	name.Type = "varchar(200)"
	name.NotNull = true
	models.AddDirective(name.Metadata, models.Directive{Namespace: "sqlite", Args: "collate NOCASE"})
	// A postgres directive on the same column must be ignored by the sqlite writer.
	models.AddDirective(name.Metadata, models.Directive{Namespace: "postgres", Args: "storage plain"})
	table.Columns["name"] = name

	models.AddDirective(table.Metadata, models.Directive{Namespace: "sqlite", Args: "without rowid"})
	models.AddDirective(table.Metadata, models.Directive{Namespace: "sqlite", Args: "strict"})
	models.AddDirective(table.Metadata, models.Directive{Namespace: "postgres", Args: "partition by RANGE (id)"})

	schema.Tables = append(schema.Tables, table)
	db.Schemas = append(db.Schemas, schema)
	return db
}

func TestSqliteDirectives_TableOptionsAndCollate(t *testing.T) {
	var buf bytes.Buffer
	w := NewWriter(&writers.WriterOptions{})
	w.writer = &buf
	if err := w.WriteDatabase(sqliteDirectiveDB(t)); err != nil {
		t.Fatalf("WriteDatabase: %v", err)
	}
	out := buf.String()

	if !strings.Contains(out, ") WITHOUT ROWID, STRICT;") {
		t.Errorf("missing table options clause:\n%s", out)
	}
	if !strings.Contains(out, `"name" TEXT COLLATE NOCASE NOT NULL`) {
		t.Errorf("missing column COLLATE clause:\n%s", out)
	}
	// postgres directives must never reach sqlite output.
	if strings.Contains(strings.ToUpper(out), "PARTITION BY") || strings.Contains(strings.ToUpper(out), "STORAGE PLAIN") {
		t.Errorf("postgres directive leaked into sqlite output:\n%s", out)
	}
}

func TestSqliteDirectives_StrictUnknownKeyErrors(t *testing.T) {
	db := sqliteDirectiveDB(t)
	models.AddDirective(db.Schemas[0].Tables[0].Metadata, models.Directive{Namespace: "sqlite", Args: "frobnicate x"})

	var buf bytes.Buffer
	w := NewWriter(&writers.WriterOptions{StrictDirectives: true})
	w.writer = &buf
	err := w.WriteDatabase(db)
	if err == nil || !strings.Contains(err.Error(), "frobnicate") {
		t.Fatalf("want strict error for unknown sqlite key, got %v", err)
	}
}

func TestSqliteDirectives_StrictIgnoresPostgres(t *testing.T) {
	var buf bytes.Buffer
	w := NewWriter(&writers.WriterOptions{StrictDirectives: true})
	w.writer = &buf
	if err := w.WriteDatabase(sqliteDirectiveDB(t)); err != nil {
		t.Fatalf("strict mode should ignore postgres directives, got %v", err)
	}
}
