package handlers

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

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

func setupInfraHandler() (*InfrastructureHandler, *mockGitRepository, *mockDNSProvider, *mockDeployer, chi.Router) {
	git := newMockGitRepository()
	dns := newMockDNSProvider()
	deployer := newMockDeployer()
	h := NewInfrastructureHandler(git, dns, deployer, nil, nil, nil, InfrastructureConfig{
		DefaultGitOwner: "threesix",
		DefaultDomain:   "threesix.ai",
		ClusterIP:       "208.122.204.172",
	})
	r := chi.NewRouter()
	r.Use(testAdminAuth)
	h.Mount(r)
	return h, git, dns, deployer, r
}

func TestInfrastructureHandler_CreateRepo(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		_, git, _, _, router := setupInfraHandler()

		body, _ := json.Marshal(CreateRepoRequest{Description: "Test repo", Private: true})
		req := httptest.NewRequest("POST", "/projects/myapp/repo", bytes.NewReader(body))
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusCreated {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusCreated)
		}
		if _, ok := git.repos["myapp"]; !ok {
			t.Error("repo not created")
		}
	})

	t.Run("invalid project id", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		req := httptest.NewRequest("POST", "/projects/INVALID_NAME!/repo", bytes.NewReader([]byte("{}")))
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusBadRequest {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
		}
	})

	t.Run("empty body allowed", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		req := httptest.NewRequest("POST", "/projects/myapp/repo", bytes.NewReader([]byte("")))
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		// Should succeed with empty body (EOF is allowed)
		if rec.Code != http.StatusCreated {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusCreated)
		}
	})

	t.Run("git not configured", func(t *testing.T) {
		h := NewInfrastructureHandler(nil, nil, nil, nil, nil, nil, InfrastructureConfig{})
		r := chi.NewRouter()
		r.Use(testAdminAuth)
		h.Mount(r)

		req := httptest.NewRequest("POST", "/projects/myapp/repo", bytes.NewReader([]byte("{}")))
		rec := httptest.NewRecorder()
		r.ServeHTTP(rec, req)

		if rec.Code != http.StatusInternalServerError {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusInternalServerError)
		}
	})
}

func TestInfrastructureHandler_GetRepo(t *testing.T) {
	t.Run("found", func(t *testing.T) {
		_, git, _, _, router := setupInfraHandler()
		git.repos["myapp"] = &domain.Repo{
			ID: 1, Owner: "threesix", Name: "myapp", FullName: "threesix/myapp",
			CloneSSH: "git@git.threesix.ai:threesix/myapp.git",
		}

		req := httptest.NewRequest("GET", "/projects/myapp/repo", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusOK {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
		}
	})

	t.Run("not found", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		req := httptest.NewRequest("GET", "/projects/missing/repo", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusNotFound {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusNotFound)
		}
	})
}

func TestInfrastructureHandler_DeleteRepo(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		_, git, _, _, router := setupInfraHandler()
		git.repos["myapp"] = &domain.Repo{ID: 1, Name: "myapp"}

		req := httptest.NewRequest("DELETE", "/projects/myapp/repo", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusOK {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
		}
	})
}

func TestInfrastructureHandler_Deploy(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		_, _, _, deployer, router := setupInfraHandler()

		body, _ := json.Marshal(DeployRequest{
			Image:    "registry.threesix.ai/myapp:latest",
			Port:     8080,
			Replicas: 2,
		})
		req := httptest.NewRequest("POST", "/projects/myapp/deploy", bytes.NewReader(body))
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusCreated {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusCreated)
		}
		if _, ok := deployer.deployments["myapp"]; !ok {
			t.Error("deployment not created")
		}
	})

	t.Run("missing image", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		body, _ := json.Marshal(DeployRequest{Port: 8080})
		req := httptest.NewRequest("POST", "/projects/myapp/deploy", bytes.NewReader(body))
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusBadRequest {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
		}
	})

	t.Run("deployer not configured", func(t *testing.T) {
		h := NewInfrastructureHandler(nil, nil, nil, nil, nil, nil, InfrastructureConfig{})
		r := chi.NewRouter()
		r.Use(testAdminAuth)
		h.Mount(r)

		body, _ := json.Marshal(DeployRequest{Image: "myimage:latest"})
		req := httptest.NewRequest("POST", "/projects/myapp/deploy", bytes.NewReader(body))
		rec := httptest.NewRecorder()
		r.ServeHTTP(rec, req)

		if rec.Code != http.StatusInternalServerError {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusInternalServerError)
		}
	})
}

func TestInfrastructureHandler_GetDeployStatus(t *testing.T) {
	t.Run("found", func(t *testing.T) {
		_, _, _, deployer, router := setupInfraHandler()
		deployer.deployments["myapp"] = &domain.DeployStatus{
			ProjectName: "myapp",
			Image:       "myimage:latest",
			Status:      domain.DeploymentStatusRunning,
			Replicas:    2,
		}

		req := httptest.NewRequest("GET", "/projects/myapp/deploy/status", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusOK {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
		}
	})

	t.Run("not found", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		req := httptest.NewRequest("GET", "/projects/missing/deploy/status", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusNotFound {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusNotFound)
		}
	})
}

func TestInfrastructureHandler_Undeploy(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		_, _, _, deployer, router := setupInfraHandler()
		deployer.deployments["myapp"] = &domain.DeployStatus{ProjectName: "myapp"}

		req := httptest.NewRequest("DELETE", "/projects/myapp/deploy", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

		if rec.Code != http.StatusOK {
			t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
		}
	})
}
