package api

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

	"github.com/ez-api/ez-api/internal/model"
	"github.com/gin-gonic/gin"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func setupAlertTestDB(t *testing.T) *gorm.DB {
	dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
	db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
	if err != nil {
		t.Fatalf("open sqlite: %v", err)
	}
	if err := db.AutoMigrate(&model.Alert{}, &model.AlertThresholdConfig{}); err != nil {
		t.Fatalf("migrate: %v", err)
	}
	return db
}

func setupAlertRouter(db *gorm.DB) *gin.Engine {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	handler := NewAlertHandler(db)

	r.GET("/admin/alerts/thresholds", handler.GetAlertThresholds)
	r.PUT("/admin/alerts/thresholds", handler.UpdateAlertThresholds)
	r.GET("/admin/alerts", handler.ListAlerts)
	r.POST("/admin/alerts", handler.CreateAlert)
	r.GET("/admin/alerts/stats", handler.GetAlertStats)

	return r
}

func TestGetAlertThresholdsDefault(t *testing.T) {
	db := setupAlertTestDB(t)
	r := setupAlertRouter(db)

	req, _ := http.NewRequest("GET", "/admin/alerts/thresholds", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("expected status 200, got %d: %s", w.Code, w.Body.String())
	}

	var resp AlertThresholdView
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal response: %v", err)
	}

	// Should return defaults
	if resp.GlobalQPS != 100 {
		t.Errorf("expected GlobalQPS=100, got %d", resp.GlobalQPS)
	}
	if resp.MasterRPM != 20 {
		t.Errorf("expected MasterRPM=20, got %d", resp.MasterRPM)
	}
	if resp.MasterRPD != 1000 {
		t.Errorf("expected MasterRPD=1000, got %d", resp.MasterRPD)
	}
	if resp.MasterTPM != 10_000_000 {
		t.Errorf("expected MasterTPM=10000000, got %d", resp.MasterTPM)
	}
	if resp.MasterTPD != 100_000_000 {
		t.Errorf("expected MasterTPD=100000000, got %d", resp.MasterTPD)
	}
}

func TestUpdateAlertThresholds(t *testing.T) {
	db := setupAlertTestDB(t)
	r := setupAlertRouter(db)

	// Update some thresholds
	body := `{"global_qps": 500, "master_rpm": 100}`
	req, _ := http.NewRequest("PUT", "/admin/alerts/thresholds", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("expected status 200, got %d: %s", w.Code, w.Body.String())
	}

	var resp AlertThresholdView
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal response: %v", err)
	}

	if resp.GlobalQPS != 500 {
		t.Errorf("expected GlobalQPS=500, got %d", resp.GlobalQPS)
	}
	if resp.MasterRPM != 100 {
		t.Errorf("expected MasterRPM=100, got %d", resp.MasterRPM)
	}
	// Other fields should remain default
	if resp.MasterRPD != 1000 {
		t.Errorf("expected MasterRPD=1000 (unchanged), got %d", resp.MasterRPD)
	}
}

func TestUpdateAlertThresholdsValidation(t *testing.T) {
	db := setupAlertTestDB(t)
	r := setupAlertRouter(db)

	tests := []struct {
		name     string
		body     string
		expected int
	}{
		{"negative global_qps", `{"global_qps": -1}`, http.StatusBadRequest},
		{"zero global_qps", `{"global_qps": 0}`, http.StatusBadRequest},
		{"negative master_rpm", `{"master_rpm": -5}`, http.StatusBadRequest},
		{"valid update", `{"global_qps": 200}`, http.StatusOK},
		{"negative min_rpm_requests_1m", `{"min_rpm_requests_1m": -1}`, http.StatusBadRequest},
		{"zero min_rpm_requests_1m allowed", `{"min_rpm_requests_1m": 0}`, http.StatusOK},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			req, _ := http.NewRequest("PUT", "/admin/alerts/thresholds", bytes.NewBufferString(tc.body))
			req.Header.Set("Content-Type", "application/json")
			w := httptest.NewRecorder()
			r.ServeHTTP(w, req)

			if w.Code != tc.expected {
				t.Errorf("expected status %d, got %d: %s", tc.expected, w.Code, w.Body.String())
			}
		})
	}
}

func TestCreateAlertWithTrafficSpikeType(t *testing.T) {
	db := setupAlertTestDB(t)
	r := setupAlertRouter(db)

	body := `{
		"type": "traffic_spike",
		"severity": "warning",
		"title": "RPM Exceeded",
		"message": "Master exceeded RPM threshold",
		"related_id": 1,
		"related_type": "master",
		"metadata": "{\"metric\":\"master_rpm\",\"value\":150,\"threshold\":20,\"window\":\"1m\"}"
	}`

	req, _ := http.NewRequest("POST", "/admin/alerts", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("expected status 201, got %d: %s", w.Code, w.Body.String())
	}

	var resp AlertView
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal response: %v", err)
	}

	if resp.Type != "traffic_spike" {
		t.Errorf("expected type=traffic_spike, got %s", resp.Type)
	}
	if resp.Severity != "warning" {
		t.Errorf("expected severity=warning, got %s", resp.Severity)
	}
	if resp.Status != "active" {
		t.Errorf("expected status=active, got %s", resp.Status)
	}
}

func TestListAlertsWithTypeFilter(t *testing.T) {
	db := setupAlertTestDB(t)
	r := setupAlertRouter(db)

	// Create some alerts
	alerts := []model.Alert{
		{Type: model.AlertTypeRateLimit, Severity: model.AlertSeverityWarning, Status: model.AlertStatusActive, Title: "Rate Limit 1"},
		{Type: model.AlertTypeTrafficSpike, Severity: model.AlertSeverityWarning, Status: model.AlertStatusActive, Title: "Traffic Spike 1"},
		{Type: model.AlertTypeTrafficSpike, Severity: model.AlertSeverityCritical, Status: model.AlertStatusActive, Title: "Traffic Spike 2"},
	}
	for _, a := range alerts {
		if err := db.Create(&a).Error; err != nil {
			t.Fatalf("create alert: %v", err)
		}
	}

	// Filter by type=traffic_spike
	req, _ := http.NewRequest("GET", "/admin/alerts?type=traffic_spike", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("expected status 200, got %d: %s", w.Code, w.Body.String())
	}

	var resp ListAlertsResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal response: %v", err)
	}

	if resp.Total != 2 {
		t.Errorf("expected 2 traffic_spike alerts, got %d", resp.Total)
	}
	for _, a := range resp.Items {
		if a.Type != "traffic_spike" {
			t.Errorf("expected type=traffic_spike, got %s", a.Type)
		}
	}
}

func TestAlertStatsIncludesAllAlerts(t *testing.T) {
	db := setupAlertTestDB(t)
	r := setupAlertRouter(db)

	// Create alerts of different types and statuses
	alerts := []model.Alert{
		{Type: model.AlertTypeRateLimit, Severity: model.AlertSeverityWarning, Status: model.AlertStatusActive, Title: "A1"},
		{Type: model.AlertTypeTrafficSpike, Severity: model.AlertSeverityCritical, Status: model.AlertStatusActive, Title: "A2"},
		{Type: model.AlertTypeErrorSpike, Severity: model.AlertSeverityInfo, Status: model.AlertStatusResolved, Title: "A3"},
	}
	for _, a := range alerts {
		if err := db.Create(&a).Error; err != nil {
			t.Fatalf("create alert: %v", err)
		}
	}

	req, _ := http.NewRequest("GET", "/admin/alerts/stats", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("expected status 200, got %d: %s", w.Code, w.Body.String())
	}

	var resp AlertStats
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal response: %v", err)
	}

	if resp.Total != 3 {
		t.Errorf("expected total=3, got %d", resp.Total)
	}
	if resp.Active != 2 {
		t.Errorf("expected active=2, got %d", resp.Active)
	}
	if resp.Resolved != 1 {
		t.Errorf("expected resolved=1, got %d", resp.Resolved)
	}
	if resp.Critical != 1 {
		t.Errorf("expected critical=1, got %d", resp.Critical)
	}
	if resp.Warning != 1 {
		t.Errorf("expected warning=1, got %d", resp.Warning)
	}
}
