package handlers

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

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

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

		req := httptest.NewRequest("POST", "/projects/myapp/deploy/restart", 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_ScaleDeploy(t *testing.T) {
	t.Run("valid scale", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		body, _ := json.Marshal(ScaleRequest{Replicas: 3})
		req := httptest.NewRequest("POST", "/projects/myapp/deploy/scale", bytes.NewReader(body))
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)

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

	t.Run("invalid replicas too high", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		body, _ := json.Marshal(ScaleRequest{Replicas: 11})
		req := httptest.NewRequest("POST", "/projects/myapp/deploy/scale", 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("invalid replicas negative", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

		body, _ := json.Marshal(ScaleRequest{Replicas: -1})
		req := httptest.NewRequest("POST", "/projects/myapp/deploy/scale", 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)
		}
	})
}

func TestInfrastructureHandler_GetDeployLogs(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		_, _, _, deployer, router := setupInfraHandler()
		deployer.logs = "line1\nline2\nline3\n"

		req := httptest.NewRequest("GET", "/projects/myapp/deploy/logs", 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_AddDomain(t *testing.T) {
	t.Run("subdomain", func(t *testing.T) {
		_, _, dns, _, router := setupInfraHandler()

		body, _ := json.Marshal(AddDomainRequest{Domain: "myapp.threesix.ai"})
		req := httptest.NewRequest("POST", "/projects/myapp/domain", 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 len(dns.records) != 1 {
			t.Errorf("DNS records = %d, want 1", len(dns.records))
		}
	})

	t.Run("external domain", func(t *testing.T) {
		_, _, dns, _, router := setupInfraHandler()

		body, _ := json.Marshal(AddDomainRequest{Domain: "myapp.example.com"})
		req := httptest.NewRequest("POST", "/projects/myapp/domain", 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)
		}
		// External domain should NOT create DNS record
		if len(dns.records) != 0 {
			t.Errorf("DNS records = %d, want 0 (external domain)", len(dns.records))
		}
	})

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

		body, _ := json.Marshal(AddDomainRequest{})
		req := httptest.NewRequest("POST", "/projects/myapp/domain", 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)
		}
	})
}

func TestInfrastructureHandler_RemoveDomain(t *testing.T) {
	t.Run("subdomain", func(t *testing.T) {
		_, _, dns, _, router := setupInfraHandler()
		dns.records["myapp"] = &domain.DNSRecord{ID: "rec-myapp", Name: "myapp"}

		req := httptest.NewRequest("DELETE", "/projects/myapp/domain?domain=myapp.threesix.ai", 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("missing domain param", func(t *testing.T) {
		_, _, _, _, router := setupInfraHandler()

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

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

func TestIsSubdomain(t *testing.T) {
	tests := []struct {
		domain, base string
		want         bool
	}{
		{"myapp.threesix.ai", "threesix.ai", true},
		{"deep.sub.threesix.ai", "threesix.ai", true},
		{"threesix.ai", "threesix.ai", false},
		{"myapp.example.com", "threesix.ai", false},
		{"", "threesix.ai", false},
	}
	for _, tt := range tests {
		t.Run(tt.domain, func(t *testing.T) {
			got := isSubdomain(tt.domain, tt.base)
			if got != tt.want {
				t.Errorf("isSubdomain(%q, %q) = %v, want %v", tt.domain, tt.base, got, tt.want)
			}
		})
	}
}

func TestGetSubdomain(t *testing.T) {
	tests := []struct {
		domain, base, want string
	}{
		{"myapp.threesix.ai", "threesix.ai", "myapp"},
		{"deep.sub.threesix.ai", "threesix.ai", "deep.sub"},
		{"threesix.ai", "threesix.ai", "threesix.ai"},
	}
	for _, tt := range tests {
		t.Run(tt.domain, func(t *testing.T) {
			got := getSubdomain(tt.domain, tt.base)
			if got != tt.want {
				t.Errorf("getSubdomain(%q, %q) = %q, want %q", tt.domain, tt.base, got, tt.want)
			}
		})
	}
}
