package service

import (
	"context"
	"fmt"
	"testing"

	"github.com/orchard9/rdev/internal/domain"
)

func TestBuildService_StartBuild(t *testing.T) {
	ctx := context.Background()

	t.Run("enqueues build successfully", func(t *testing.T) {
		queue := newMockWorkQueue()
		audit := newMockBuildAudit()
		svc := NewBuildService(queue, audit)

		taskID, err := svc.StartBuild(ctx, "project-1", domain.BuildSpec{
			Prompt:   "Build a landing page",
			Template: "nextjs",
		})
		if err != nil {
			t.Fatalf("StartBuild() error = %v", err)
		}
		if taskID == "" {
			t.Error("expected non-empty task ID")
		}

		// Verify task was enqueued
		if len(queue.tasks) != 1 {
			t.Errorf("expected 1 task in queue, got %d", len(queue.tasks))
		}
		task := queue.tasks[taskID]
		if task.ProjectID != "project-1" {
			t.Errorf("got project_id %q, want %q", task.ProjectID, "project-1")
		}
		if task.Type != domain.WorkTaskTypeBuild {
			t.Errorf("got type %q, want %q", task.Type, domain.WorkTaskTypeBuild)
		}

		// Verify audit was recorded
		if len(audit.entries) != 1 {
			t.Errorf("expected 1 audit entry, got %d", len(audit.entries))
		}
	})

	t.Run("validates prompt required", func(t *testing.T) {
		queue := newMockWorkQueue()
		audit := newMockBuildAudit()
		svc := NewBuildService(queue, audit)

		_, err := svc.StartBuild(ctx, "project-1", domain.BuildSpec{})
		if err == nil {
			t.Error("expected error for empty prompt")
		}
	})

	t.Run("validates project ID required", func(t *testing.T) {
		queue := newMockWorkQueue()
		audit := newMockBuildAudit()
		svc := NewBuildService(queue, audit)

		_, err := svc.StartBuild(ctx, "", domain.BuildSpec{Prompt: "Build"})
		if err == nil {
			t.Error("expected error for empty project ID")
		}
	})

	t.Run("includes variables in spec", func(t *testing.T) {
		queue := newMockWorkQueue()
		audit := newMockBuildAudit()
		svc := NewBuildService(queue, audit)

		taskID, err := svc.StartBuild(ctx, "project-1", domain.BuildSpec{
			Prompt: "Build",
			Variables: map[string]string{
				"name":  "My App",
				"color": "blue",
			},
		})
		if err != nil {
			t.Fatalf("StartBuild() error = %v", err)
		}

		task := queue.tasks[taskID]
		vars, ok := task.Spec["variables"].(map[string]string)
		if !ok {
			t.Fatal("expected variables in task spec")
		}
		if vars["name"] != "My App" {
			t.Errorf("got variable name %q, want %q", vars["name"], "My App")
		}
	})

	t.Run("continues if audit fails", func(t *testing.T) {
		queue := newMockWorkQueue()
		audit := newMockBuildAudit()
		audit.err = fmt.Errorf("db connection failed")
		svc := NewBuildService(queue, audit)

		taskID, err := svc.StartBuild(ctx, "project-1", domain.BuildSpec{
			Prompt: "Build",
		})
		if err != nil {
			t.Fatalf("StartBuild() should succeed even if audit fails, got error = %v", err)
		}
		if taskID == "" {
			t.Error("expected non-empty task ID")
		}
	})
}

func TestBuildService_GetBuildStatus(t *testing.T) {
	ctx := context.Background()

	t.Run("returns existing entry", func(t *testing.T) {
		audit := newMockBuildAudit()
		audit.entries["task-1"] = &domain.BuildAuditEntry{
			TaskID:    "task-1",
			ProjectID: "project-1",
			Status:    domain.BuildStatusRunning,
		}
		svc := NewBuildService(newMockWorkQueue(), audit)

		entry, err := svc.GetBuildStatus(ctx, "task-1")
		if err != nil {
			t.Fatalf("GetBuildStatus() error = %v", err)
		}
		if entry.Status != domain.BuildStatusRunning {
			t.Errorf("got status %q, want %q", entry.Status, domain.BuildStatusRunning)
		}
	})

	t.Run("returns error for nonexistent entry", func(t *testing.T) {
		audit := newMockBuildAudit()
		svc := NewBuildService(newMockWorkQueue(), audit)

		_, err := svc.GetBuildStatus(ctx, "nonexistent")
		if err == nil {
			t.Error("expected error for nonexistent entry")
		}
	})
}

func TestBuildService_ListBuilds(t *testing.T) {
	ctx := context.Background()

	audit := newMockBuildAudit()
	audit.entries["task-1"] = &domain.BuildAuditEntry{
		TaskID: "task-1", ProjectID: "project-a", Status: domain.BuildStatusCompleted,
	}
	audit.entries["task-2"] = &domain.BuildAuditEntry{
		TaskID: "task-2", ProjectID: "project-a", Status: domain.BuildStatusFailed,
	}
	audit.entries["task-3"] = &domain.BuildAuditEntry{
		TaskID: "task-3", ProjectID: "project-b", Status: domain.BuildStatusPending,
	}

	svc := NewBuildService(newMockWorkQueue(), audit)

	t.Run("lists builds for project", func(t *testing.T) {
		entries, err := svc.ListBuilds(ctx, "project-a", 50)
		if err != nil {
			t.Fatalf("ListBuilds() error = %v", err)
		}
		if len(entries) != 2 {
			t.Errorf("got %d entries, want 2", len(entries))
		}
	})

	t.Run("uses default limit", func(t *testing.T) {
		entries, err := svc.ListBuilds(ctx, "project-a", 0)
		if err != nil {
			t.Fatalf("ListBuilds() error = %v", err)
		}
		if len(entries) != 2 {
			t.Errorf("got %d entries, want 2", len(entries))
		}
	})
}

func TestBuildService_CompleteBuild(t *testing.T) {
	ctx := context.Background()

	t.Run("updates audit on completion", func(t *testing.T) {
		audit := newMockBuildAudit()
		audit.entries["task-1"] = &domain.BuildAuditEntry{
			TaskID:    "task-1",
			ProjectID: "project-1",
			Status:    domain.BuildStatusRunning,
		}
		svc := NewBuildService(newMockWorkQueue(), audit)

		err := svc.CompleteBuild(ctx, "task-1", &domain.BuildResult{
			Success:    true,
			CommitSHA:  "abc123",
			DurationMs: 5000,
		})
		if err != nil {
			t.Fatalf("CompleteBuild() error = %v", err)
		}

		entry := audit.entries["task-1"]
		if entry.Status != domain.BuildStatusCompleted {
			t.Errorf("got status %q, want %q", entry.Status, domain.BuildStatusCompleted)
		}
		if entry.Result == nil {
			t.Fatal("expected result to be set")
		}
		if !entry.Result.Success {
			t.Error("expected result.Success = true")
		}
	})
}
