package integration

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

	"github.com/atlasos/calypso/internal/common/config"
	"github.com/atlasos/calypso/internal/common/logger"
	"github.com/atlasos/calypso/internal/common/password"
	"github.com/atlasos/calypso/internal/common/router"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestMain(m *testing.M) {
	// Set Gin to test mode
	gin.SetMode(gin.TestMode)
	
	// Run tests
	code := m.Run()
	
	// Cleanup
	if TestDB != nil {
		TestDB.Close()
	}
	
	os.Exit(code)
}

func TestHealthEndpoint(t *testing.T) {
	db := SetupTestDB(t)
	defer CleanupTestDB(t)

	cfg := TestConfig
	if cfg == nil {
		cfg = &config.Config{
			Server: config.ServerConfig{
				Port: 8080,
			},
		}
	}

	log := TestLogger
	if log == nil {
		log = logger.NewLogger("test")
	}
	r := router.NewRouter(cfg, db, log)

	req := httptest.NewRequest("GET", "/api/v1/health", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Equal(t, "calypso-api", response["service"])
}

func TestLoginEndpoint(t *testing.T) {
	db := SetupTestDB(t)
	defer CleanupTestDB(t)

	// Create test user
	passwordHash, err := password.HashPassword("testpass123", config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	})
	require.NoError(t, err)

	userID := CreateTestUser(t, "testuser", "test@example.com", passwordHash, false)

	cfg := TestConfig
	if cfg == nil {
		cfg = &config.Config{
			Server: config.ServerConfig{
				Port: 8080,
			},
			Auth: config.AuthConfig{
				JWTSecret:     "test-jwt-secret-key-minimum-32-characters-long",
				TokenLifetime: 24 * 60 * 60 * 1000000000, // 24 hours in nanoseconds
			},
		}
	}

	log := TestLogger
	if log == nil {
		log = logger.NewLogger("test")
	}
	r := router.NewRouter(cfg, db, log)

	// Test login
	loginData := map[string]string{
		"username": "testuser",
		"password": "testpass123",
	}
	jsonData, _ := json.Marshal(loginData)

	req := httptest.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(jsonData))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.NotEmpty(t, response["token"])
	assert.Equal(t, userID, response["user"].(map[string]interface{})["id"])
}

func TestLoginEndpoint_WrongPassword(t *testing.T) {
	db := SetupTestDB(t)
	defer CleanupTestDB(t)

	// Create test user
	passwordHash, err := password.HashPassword("testpass123", config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	})
	require.NoError(t, err)

	CreateTestUser(t, "testuser", "test@example.com", passwordHash, false)

	cfg := TestConfig
	if cfg == nil {
		cfg = &config.Config{
			Server: config.ServerConfig{
				Port: 8080,
			},
			Auth: config.AuthConfig{
				JWTSecret:     "test-jwt-secret-key-minimum-32-characters-long",
				TokenLifetime: 24 * 60 * 60 * 1000000000,
			},
		}
	}

	log := TestLogger
	if log == nil {
		log = logger.NewLogger("test")
	}
	r := router.NewRouter(cfg, db, log)

	// Test login with wrong password
	loginData := map[string]string{
		"username": "testuser",
		"password": "wrongpassword",
	}
	jsonData, _ := json.Marshal(loginData)

	req := httptest.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(jsonData))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

func TestGetCurrentUser(t *testing.T) {
	db := SetupTestDB(t)
	defer CleanupTestDB(t)

	// Create test user and get token
	passwordHash, err := password.HashPassword("testpass123", config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	})
	require.NoError(t, err)

	userID := CreateTestUser(t, "testuser", "test@example.com", passwordHash, false)

	cfg := TestConfig
	if cfg == nil {
		cfg = &config.Config{
			Server: config.ServerConfig{
				Port: 8080,
			},
			Auth: config.AuthConfig{
				JWTSecret:     "test-jwt-secret-key-minimum-32-characters-long",
				TokenLifetime: 24 * 60 * 60 * 1000000000,
			},
		}
	}

	log := TestLogger
	if log == nil {
		log = logger.NewLogger("test")
	}

	// Login to get token
	loginData := map[string]string{
		"username": "testuser",
		"password": "testpass123",
	}
	jsonData, _ := json.Marshal(loginData)

	r := router.NewRouter(cfg, db, log)
	req := httptest.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(jsonData))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var loginResponse map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &loginResponse)
	require.NoError(t, err)
	token := loginResponse["token"].(string)

	// Test /auth/me endpoint (use same router instance)
	req2 := httptest.NewRequest("GET", "/api/v1/auth/me", nil)
	req2.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	w2 := httptest.NewRecorder()
	r.ServeHTTP(w2, req2)

	assert.Equal(t, http.StatusOK, w2.Code)

	var userResponse map[string]interface{}
	err = json.Unmarshal(w2.Body.Bytes(), &userResponse)
	require.NoError(t, err)
	assert.Equal(t, userID, userResponse["id"])
	assert.Equal(t, "testuser", userResponse["username"])
}

func TestListAlerts(t *testing.T) {
	db := SetupTestDB(t)
	defer CleanupTestDB(t)

	// Create test user and get token
	passwordHash, err := password.HashPassword("testpass123", config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	})
	require.NoError(t, err)

	CreateTestUser(t, "testuser", "test@example.com", passwordHash, true) // Admin user

	cfg := TestConfig
	if cfg == nil {
		cfg = &config.Config{
			Server: config.ServerConfig{
				Port: 8080,
			},
			Auth: config.AuthConfig{
				JWTSecret:     "test-jwt-secret-key-minimum-32-characters-long",
				TokenLifetime: 24 * 60 * 60 * 1000000000,
			},
		}
	}

	log := TestLogger
	if log == nil {
		log = logger.NewLogger("test")
	}
	r := router.NewRouter(cfg, db, log)

	// Login to get token
	loginData := map[string]string{
		"username": "testuser",
		"password": "testpass123",
	}
	jsonData, _ := json.Marshal(loginData)

	req := httptest.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(jsonData))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var loginResponse map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &loginResponse)
	require.NoError(t, err)
	token := loginResponse["token"].(string)

	// Test /monitoring/alerts endpoint (use same router instance)
	req2 := httptest.NewRequest("GET", "/api/v1/monitoring/alerts", nil)
	req2.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	w2 := httptest.NewRecorder()
	r.ServeHTTP(w2, req2)

	assert.Equal(t, http.StatusOK, w2.Code)

	var alertsResponse map[string]interface{}
	err = json.Unmarshal(w2.Body.Bytes(), &alertsResponse)
	require.NoError(t, err)
	assert.NotNil(t, alertsResponse["alerts"])
}

