package store_test

import (
	"context"
	"database/sql"
	"path/filepath"
	"testing"
	"time"

	_ "modernc.org/sqlite"

	"go.uber.org/zap"

	"iop/apps/node/internal/store"
)

const oldRunsSchema = `
CREATE TABLE runs (
    run_id       TEXT     PRIMARY KEY,
    adapter      TEXT     NOT NULL,
    model        TEXT     NOT NULL,
    status       TEXT     NOT NULL DEFAULT 'running',
    created_at   DATETIME NOT NULL,
    completed_at DATETIME,
    error        TEXT
);
`

func seedOldSchemaDB(t *testing.T) string {
	t.Helper()
	dir := t.TempDir()
	dsn := filepath.Join(dir, "old.db")
	db, err := sql.Open("sqlite", dsn)
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	if _, err := db.Exec(oldRunsSchema); err != nil {
		t.Fatalf("seed schema: %v", err)
	}
	if err := db.Close(); err != nil {
		t.Fatalf("close: %v", err)
	}
	return dsn
}

func newTestStore(t *testing.T) *store.Store {
	t.Helper()
	s, err := store.New(":memory:", zap.NewNop())
	if err != nil {
		t.Fatalf("store.New: %v", err)
	}
	t.Cleanup(func() { _ = s.Close() })
	return s
}

func TestStore_RunSessionFields(t *testing.T) {
	s := newTestStore(t)
	ctx := context.Background()

	r := store.RunRecord{
		RunID:      "run-1",
		Adapter:    "cli",
		Target:     "codex",
		SessionID:  "session-a",
		Background: true,
		Status:     "running",
		CreatedAt:  time.Now(),
	}
	if err := s.InsertRun(ctx, r); err != nil {
		t.Fatalf("InsertRun: %v", err)
	}

	got, err := s.GetRun(ctx, "run-1")
	if err != nil {
		t.Fatalf("GetRun: %v", err)
	}
	if got == nil {
		t.Fatal("expected run record")
	}
	if got.SessionID != "session-a" {
		t.Errorf("SessionID: got %q want %q", got.SessionID, "session-a")
	}
	if !got.Background {
		t.Error("Background: expected true")
	}
	if got.Status != "running" {
		t.Errorf("Status: got %q want %q", got.Status, "running")
	}

	if err := s.CompleteRun(ctx, "run-1", "cancelled", ""); err != nil {
		t.Fatalf("CompleteRun: %v", err)
	}
	got, err = s.GetRun(ctx, "run-1")
	if err != nil {
		t.Fatalf("GetRun after complete: %v", err)
	}
	if got.Status != "cancelled" {
		t.Errorf("Status after cancel: got %q want %q", got.Status, "cancelled")
	}
}

func TestStore_DefaultSessionID(t *testing.T) {
	s := newTestStore(t)
	ctx := context.Background()

	r := store.RunRecord{
		RunID:     "run-2",
		Adapter:   "cli",
		Target:    "codex",
		SessionID: "", // empty → should default to "default"
		Status:    "running",
		CreatedAt: time.Now(),
	}
	if err := s.InsertRun(ctx, r); err != nil {
		t.Fatalf("InsertRun: %v", err)
	}

	got, err := s.GetRun(ctx, "run-2")
	if err != nil {
		t.Fatalf("GetRun: %v", err)
	}
	if got.SessionID != "default" {
		t.Errorf("SessionID: got %q want %q", got.SessionID, "default")
	}
}

func TestStore_MigratesExistingRunsTable(t *testing.T) {
	dsn := seedOldSchemaDB(t)

	s, err := store.New(dsn, zap.NewNop())
	if err != nil {
		t.Fatalf("store.New: %v", err)
	}
	t.Cleanup(func() { _ = s.Close() })

	ctx := context.Background()
	r := store.RunRecord{
		RunID:      "run-mig",
		Adapter:    "cli",
		Target:     "codex",
		SessionID:  "session-a",
		Background: true,
		Status:     "running",
		CreatedAt:  time.Now(),
	}
	if err := s.InsertRun(ctx, r); err != nil {
		t.Fatalf("InsertRun: %v", err)
	}

	got, err := s.GetRun(ctx, "run-mig")
	if err != nil {
		t.Fatalf("GetRun: %v", err)
	}
	if got == nil {
		t.Fatal("expected run record")
	}
	if got.SessionID != "session-a" {
		t.Errorf("SessionID: got %q want %q", got.SessionID, "session-a")
	}
	if !got.Background {
		t.Error("Background: expected true")
	}
}

func TestStore_MigrationIsIdempotent(t *testing.T) {
	dsn := seedOldSchemaDB(t)

	s1, err := store.New(dsn, zap.NewNop())
	if err != nil {
		t.Fatalf("store.New first: %v", err)
	}
	if err := s1.Close(); err != nil {
		t.Fatalf("close first: %v", err)
	}

	s2, err := store.New(dsn, zap.NewNop())
	if err != nil {
		t.Fatalf("store.New second: %v", err)
	}
	t.Cleanup(func() { _ = s2.Close() })
}
