package schedule_test

import (
	"context"
	"encoding/json"
	"log/slog"
	"os"
	"strings"
	"testing"

	"github.com/jackc/pgx/v5/pgtype"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/memohai/memoh/internal/boot"
	"github.com/memohai/memoh/internal/db"
	"github.com/memohai/memoh/internal/db/sqlc"
	"github.com/memohai/memoh/internal/schedule"
)

func setupScheduleIntegrationTest(t *testing.T) (*schedule.Service, *sqlc.Queries, *pgxpool.Pool, *mockTriggerer, func()) {
	t.Helper()

	dsn := os.Getenv("TEST_POSTGRES_DSN")
	if dsn == "" {
		t.Skip("skip integration test: TEST_POSTGRES_DSN is not set")
	}

	ctx := context.Background()
	pool, err := pgxpool.New(ctx, dsn)
	if err != nil {
		t.Skipf("skip integration test: cannot connect to database: %v", err)
	}
	if err := pool.Ping(ctx); err != nil {
		pool.Close()
		t.Skipf("skip integration test: database ping failed: %v", err)
	}

	queries := sqlc.New(pool)
	mock := &mockTriggerer{}
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	cfg := &boot.RuntimeConfig{JwtSecret: "integration-test-jwt-secret"}
	svc := schedule.NewService(logger, queries, mock, cfg)

	return svc, queries, pool, mock, func() { pool.Close() }
}

type mockTriggerer struct {
	called  bool
	botID   string
	payload schedule.TriggerPayload
	token   string
}

func (m *mockTriggerer) TriggerSchedule(_ context.Context, botID string, payload schedule.TriggerPayload, token string) error {
	m.called = true
	m.botID = botID
	m.payload = payload
	m.token = token
	return nil
}

func createUserBotAndSchedule(ctx context.Context, t *testing.T, queries *sqlc.Queries) (ownerUserID, botID, scheduleID string) {
	t.Helper()

	userRow, err := queries.CreateUser(ctx, sqlc.CreateUserParams{
		IsActive: true,
		Metadata: []byte("{}"),
	})
	if err != nil {
		t.Fatalf("create user: %v", err)
	}
	ownerUserID = userRow.ID.String()

	pgOwnerID, err := db.ParseUUID(ownerUserID)
	if err != nil {
		t.Fatalf("parse owner uuid: %v", err)
	}
	meta, _ := json.Marshal(map[string]any{"source": "schedule-integration-test"})
	botRow, err := queries.CreateBot(ctx, sqlc.CreateBotParams{
		OwnerUserID: pgOwnerID,
		Type:        "personal",
		DisplayName: pgtype.Text{String: "schedule-test-bot", Valid: true},
		AvatarUrl:   pgtype.Text{},
		IsActive:    true,
		Metadata:    meta,
		Status:      "ready",
	})
	if err != nil {
		t.Fatalf("create bot: %v", err)
	}
	botID = botRow.ID.String()

	pgBotID, err := db.ParseUUID(botID)
	if err != nil {
		t.Fatalf("parse bot uuid: %v", err)
	}
	schedRow, err := queries.CreateSchedule(ctx, sqlc.CreateScheduleParams{
		Name:        "integration-daily",
		Description: "daily job for integration test",
		Pattern:     "0 0 * * *",
		MaxCalls:    pgtype.Int4{Valid: false},
		Enabled:     true,
		Command:     "run daily report",
		BotID:       pgBotID,
	})
	if err != nil {
		t.Fatalf("create schedule: %v", err)
	}
	scheduleID = schedRow.ID.String()
	return ownerUserID, botID, scheduleID
}

func cleanupScheduleTestData(ctx context.Context, t *testing.T, queries *sqlc.Queries, pool *pgxpool.Pool, ownerUserID, botID, scheduleID string) {
	t.Helper()
	schedID, _ := db.ParseUUID(scheduleID)
	_ = queries.DeleteSchedule(ctx, schedID)
	botUUID, _ := db.ParseUUID(botID)
	_ = queries.DeleteBotByID(ctx, botUUID)
	userUUID, _ := db.ParseUUID(ownerUserID)
	_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = $1", userUUID)
}

func TestIntegrationTrigger_CallsTriggererWithCorrectPayload(t *testing.T) {
	svc, queries, pool, mock, cleanup := setupScheduleIntegrationTest(t)
	defer cleanup()

	ctx := context.Background()
	ownerUserID, botID, scheduleID := createUserBotAndSchedule(ctx, t, queries)
	defer cleanupScheduleTestData(ctx, t, queries, pool, ownerUserID, botID, scheduleID)

	err := svc.Trigger(ctx, scheduleID)
	if err != nil {
		t.Fatalf("Trigger failed: %v", err)
	}

	if !mock.called {
		t.Fatal("triggerer was not called")
	}
	if mock.botID != botID {
		t.Errorf("triggerer botID = %s, want %s", mock.botID, botID)
	}
	if mock.payload.ID != scheduleID {
		t.Errorf("payload.ID = %s, want %s", mock.payload.ID, scheduleID)
	}
	if mock.payload.Name != "integration-daily" {
		t.Errorf("payload.Name = %s, want integration-daily", mock.payload.Name)
	}
	if mock.payload.Command != "run daily report" {
		t.Errorf("payload.Command = %s, want run daily report", mock.payload.Command)
	}
	if mock.payload.OwnerUserID != ownerUserID {
		t.Errorf("payload.OwnerUserID = %s, want %s", mock.payload.OwnerUserID, ownerUserID)
	}
	if !strings.HasPrefix(mock.token, "Bearer ") {
		t.Errorf("token should have Bearer prefix, got: %s", mock.token)
	}
}
