package api

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

	"github.com/golang-jwt/jwt/v5"

	"github.com/pulse-lets-go/internal/config"
	"github.com/pulse-lets-go/internal/engine"
)

func testCfg() *config.Config {
	return &config.Config{
		StaleThresholdSec: 20,
		Log:               config.LogConfig{Level: "info", Format: "text"},
		RateLimit:         config.RateLimitConfig{Enabled: false},
		BalanceMode:       "weighted_random",
		Scoring: config.ScoringConfig{
			IdleCPUMin:            5.0,
			CallFailureRateLethal: 15.0,
			Weights:               config.ScoringWeights{CallScore: 0.40, LoadScore: 0.30, IdleScore: 0.20, FailScore: 0.10},
		},
	}
}

func createTestJWT(secret, username, role string) string {
	claims := jwt.MapClaims{
		"user_id":  "user-test",
		"username": username,
		"role":     role,
		"exp":      time.Now().Add(1 * time.Hour).Unix(),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	s, _ := token.SignedString([]byte(secret))
	return s
}

func TestRouteModeEndpoint_NoAuth(t *testing.T) {
	cfg := testCfg()
	eng := engine.NewEngine(cfg)
	a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg)
	handler := a.Handler()

	body, _ := json.Marshal(map[string]string{"mode": "best"})
	req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if rr.Code == 404 {
		t.Fatalf("PUT /api/route/mode вернул 404 — маршрут не найден! body=%s", rr.Body.String())
	}
	if rr.Code == 401 {
		t.Log("OK: без токена получаем 401 (роутинг работает)")
	} else {
		t.Logf("Код ответа: %d, body: %s", rr.Code, rr.Body.String())
	}
}

func TestRouteModeEndpoint_ViewerForbidden(t *testing.T) {
	cfg := testCfg()
	eng := engine.NewEngine(cfg)
	a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg)
	handler := a.Handler()

	token := createTestJWT("test-secret", "pbx-viewer", "viewer")
	body, _ := json.Marshal(map[string]string{"mode": "weighted_random"})
	req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if rr.Code == 404 {
		t.Fatalf("PUT /api/route/mode вернул 404 (viewer) — маршрут не найден!")
	}
	if rr.Code != 403 {
		t.Errorf("viewer должен получить 403, получен %d", rr.Code)
	}
	t.Logf("viewer: код=%d, body=%s", rr.Code, rr.Body.String())
}

func TestRouteModeEndpoint_AdminSuccess(t *testing.T) {
	cfg := testCfg()
	eng := engine.NewEngine(cfg)
	a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg)
	handler := a.Handler()

	token := createTestJWT("test-secret", "admin", "admin")
	body, _ := json.Marshal(map[string]string{"mode": "best"})
	req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if rr.Code == 404 {
		t.Fatalf("PUT /api/route/mode вернул 404 (admin) — маршрут не найден!")
	}
	if rr.Code != 200 {
		t.Fatalf("admin должен получить 200, получен %d body=%s", rr.Code, rr.Body.String())
	}

	var resp map[string]string
	json.Unmarshal(rr.Body.Bytes(), &resp)
	if resp["mode"] != "best" {
		t.Errorf("ожидался mode=best, получен %s", resp["mode"])
	}

	if eng.GetBalancingMode() != "best" {
		t.Errorf("engine mode должен быть 'best', получен %s", eng.GetBalancingMode())
	}

	t.Logf("admin: код=%d, body=%s, engine_mode=%s", rr.Code, rr.Body.String(), eng.GetBalancingMode())
}

func TestRouteModeEndpoint_InvalidMode(t *testing.T) {
	cfg := testCfg()
	eng := engine.NewEngine(cfg)
	a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg)
	handler := a.Handler()

	token := createTestJWT("test-secret", "admin", "admin")
	body, _ := json.Marshal(map[string]string{"mode": "invalid"})
	req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if rr.Code != 400 {
		t.Errorf("невалидный mode должен дать 400, получен %d", rr.Code)
	}
}

func TestRouteEndpoint_UsesPickNode(t *testing.T) {
	cfg := testCfg()
	eng := engine.NewEngine(cfg)
	a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg)
	a.balanceGateways["test-node"] = "sip:test:5060"
	a.fallbackGateway = "sip:fallback:5060"
	handler := a.Handler()

	req := httptest.NewRequest("GET", "/api/route", nil)
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)
	if rr.Code != 503 {
		t.Errorf("GET /api/route без нод должен дать 503, получен %d", rr.Code)
	}
}
