package handlers

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/adapter/memory"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/service"
)

func TestVerifyHandler_Submit_Success(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	body := SubmitVerifyRequest{
		ProjectID: "my-project",
		URL:       "https://example.com",
		Viewports: []string{"1920x1080", "375x667"},
	}
	bodyBytes, _ := json.Marshal(body)

	req := httptest.NewRequest(http.MethodPost, "/verify", bytes.NewReader(bodyBytes))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusCreated {
		t.Errorf("got status %d, want %d; body: %s", rec.Code, http.StatusCreated, rec.Body.String())
	}

	var resp map[string]any
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}
	data, ok := resp["data"].(map[string]any)
	if !ok {
		t.Fatalf("expected data to be map, got %T", resp["data"])
	}
	if data["task_id"] == nil || data["task_id"] == "" {
		t.Error("expected task_id in response")
	}
	if data["status_url"] == nil {
		t.Error("expected status_url in response")
	}
	if data["stream_url"] == nil {
		t.Error("expected stream_url in response")
	}
}

func TestVerifyHandler_Submit_MissingURL(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	body := SubmitVerifyRequest{
		ProjectID: "my-project",
		// URL missing
	}
	bodyBytes, _ := json.Marshal(body)

	req := httptest.NewRequest(http.MethodPost, "/verify", bytes.NewReader(bodyBytes))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusBadRequest {
		t.Errorf("got status %d, want %d; body: %s", rec.Code, http.StatusBadRequest, rec.Body.String())
	}
}

func TestVerifyHandler_Submit_InvalidURL(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	body := SubmitVerifyRequest{
		ProjectID: "my-project",
		URL:       "not-a-valid-url",
	}
	bodyBytes, _ := json.Marshal(body)

	req := httptest.NewRequest(http.MethodPost, "/verify", bytes.NewReader(bodyBytes))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusInternalServerError {
		// Invalid URL scheme gets caught as an internal error after service processes
		t.Logf("got status %d; body: %s", rec.Code, rec.Body.String())
	}
}

func TestVerifyHandler_Get_Found(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	// Pre-populate a verify task
	queue.tasks["verify-task-1"] = &domain.WorkTask{
		ID:        "verify-task-1",
		ProjectID: "my-project",
		Type:      domain.WorkTaskTypeVerify,
		Status:    domain.WorkTaskStatusCompleted,
		Spec: map[string]any{
			"url":       "https://example.com",
			"viewports": []string{"1920x1080"},
		},
		Result: &domain.WorkResult{
			Artifacts: map[string]string{
				"screenshot_1920x1080": "/captures/verify-task-1/1920_1080.png",
				"duration_ms":          "2500",
			},
		},
		CreatedAt: time.Now(),
	}

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	req := httptest.NewRequest(http.MethodGet, "/verify/verify-task-1", nil)
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("got status %d, want %d; body: %s", rec.Code, http.StatusOK, rec.Body.String())
	}

	var resp map[string]any
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}
	data, ok := resp["data"].(map[string]any)
	if !ok {
		t.Fatalf("expected data to be map, got %T", resp["data"])
	}
	if data["id"] != "verify-task-1" {
		t.Errorf("got id=%v, want verify-task-1", data["id"])
	}
	if data["status"] != "completed" {
		t.Errorf("got status=%v, want completed", data["status"])
	}
	if data["url"] != "https://example.com" {
		t.Errorf("got url=%v, want https://example.com", data["url"])
	}
	screenshots, ok := data["screenshots"].(map[string]any)
	if !ok {
		t.Fatalf("expected screenshots to be map, got %T", data["screenshots"])
	}
	if screenshots["1920x1080"] == nil {
		t.Error("expected screenshot for 1920x1080 viewport")
	}
}

func TestVerifyHandler_Get_NotFound(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	req := httptest.NewRequest(http.MethodGet, "/verify/nonexistent", nil)
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusNotFound {
		t.Errorf("got status %d, want %d; body: %s", rec.Code, http.StatusNotFound, rec.Body.String())
	}
}

func TestVerifyHandler_Cancel_Pending(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	// Pre-populate a pending verify task
	queue.tasks["verify-task-2"] = &domain.WorkTask{
		ID:        "verify-task-2",
		ProjectID: "my-project",
		Type:      domain.WorkTaskTypeVerify,
		Status:    domain.WorkTaskStatusPending,
		Spec: map[string]any{
			"url": "https://example.com",
		},
		CreatedAt: time.Now(),
	}

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	req := httptest.NewRequest(http.MethodDelete, "/verify/verify-task-2", nil)
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("got status %d, want %d; body: %s", rec.Code, http.StatusOK, rec.Body.String())
	}

	// Verify task was cancelled
	if queue.tasks["verify-task-2"].Status != domain.WorkTaskStatusCancelled {
		t.Errorf("expected task status to be cancelled, got %s", queue.tasks["verify-task-2"].Status)
	}
}

func TestVerifyHandler_Cancel_NotPending(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	// Pre-populate a running verify task (can't be cancelled)
	queue.tasks["verify-task-3"] = &domain.WorkTask{
		ID:        "verify-task-3",
		ProjectID: "my-project",
		Type:      domain.WorkTaskTypeVerify,
		Status:    domain.WorkTaskStatusRunning,
		Spec: map[string]any{
			"url": "https://example.com",
		},
		CreatedAt: time.Now(),
	}

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	req := httptest.NewRequest(http.MethodDelete, "/verify/verify-task-3", nil)
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	// Should fail because task is not pending
	if rec.Code != http.StatusNotFound && rec.Code != http.StatusBadRequest {
		t.Errorf("got status %d, want 404 or 400; body: %s", rec.Code, rec.Body.String())
	}
}

func TestVerifyHandler_ListByProject(t *testing.T) {
	queue := newMockWorkQueue()
	verifyService := service.NewVerifyService(queue)
	streams := memory.NewStreamPublisher()
	handler := NewVerifyHandler(verifyService, streams)

	// Pre-populate verify tasks for different projects
	queue.tasks["verify-1"] = &domain.WorkTask{
		ID:        "verify-1",
		ProjectID: "project-a",
		Type:      domain.WorkTaskTypeVerify,
		Status:    domain.WorkTaskStatusCompleted,
		Spec:      map[string]any{"url": "https://example.com/1"},
		CreatedAt: time.Now(),
	}
	queue.tasks["verify-2"] = &domain.WorkTask{
		ID:        "verify-2",
		ProjectID: "project-a",
		Type:      domain.WorkTaskTypeVerify,
		Status:    domain.WorkTaskStatusPending,
		Spec:      map[string]any{"url": "https://example.com/2"},
		CreatedAt: time.Now(),
	}
	queue.tasks["verify-3"] = &domain.WorkTask{
		ID:        "verify-3",
		ProjectID: "project-b",
		Type:      domain.WorkTaskTypeVerify,
		Status:    domain.WorkTaskStatusCompleted,
		Spec:      map[string]any{"url": "https://example.com/3"},
		CreatedAt: time.Now(),
	}
	// Non-verify task should be filtered out
	queue.tasks["build-1"] = &domain.WorkTask{
		ID:        "build-1",
		ProjectID: "project-a",
		Type:      domain.WorkTaskTypeBuild,
		Status:    domain.WorkTaskStatusCompleted,
		Spec:      map[string]any{"prompt": "build something"},
		CreatedAt: time.Now(),
	}

	router := chi.NewRouter()
	router.Use(testAdminAuth)
	handler.Mount(router)

	req := httptest.NewRequest(http.MethodGet, "/projects/project-a/verify", nil)
	rec := httptest.NewRecorder()

	router.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("got status %d, want %d; body: %s", rec.Code, http.StatusOK, rec.Body.String())
	}

	var resp map[string]any
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}
	data, ok := resp["data"].(map[string]any)
	if !ok {
		t.Fatalf("expected data to be map, got %T", resp["data"])
	}
	tasks, ok := data["tasks"].([]any)
	if !ok {
		t.Fatalf("expected tasks to be array, got %T", data["tasks"])
	}
	// Should only get 2 verify tasks for project-a (build task filtered out)
	if len(tasks) != 2 {
		t.Errorf("got %d tasks, want 2", len(tasks))
	}
	if data["project_id"] != "project-a" {
		t.Errorf("got project_id=%v, want project-a", data["project_id"])
	}
}
