package router

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

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// setupTestDB creates an in-memory SQLite database for health check tests
func setupTestDB(t *testing.T) *gorm.DB {
	t.Helper()
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	require.NoError(t, err)
	return db
}

func TestLiveCheck_Returns200(t *testing.T) {
	e := echo.New()
	e.GET("/api/health/live", liveCheck)

	req := httptest.NewRequest(http.MethodGet, "/api/health/live", nil)
	rec := httptest.NewRecorder()
	e.ServeHTTP(rec, req)

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

	var resp map[string]interface{}
	err := json.Unmarshal(rec.Body.Bytes(), &resp)
	require.NoError(t, err)

	assert.Equal(t, "alive", resp["status"])
	assert.Equal(t, Version, resp["version"])
	assert.Contains(t, resp, "timestamp")
}

func TestReadinessCheck_HealthyDB_NilCache_Returns200(t *testing.T) {
	db := setupTestDB(t)

	deps := &Dependencies{
		DB:    db,
		Cache: nil, // No Redis configured
	}

	e := echo.New()
	e.GET("/api/health/", readinessCheck(deps))

	req := httptest.NewRequest(http.MethodGet, "/api/health/", nil)
	rec := httptest.NewRecorder()
	e.ServeHTTP(rec, req)

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

	var resp map[string]interface{}
	err := json.Unmarshal(rec.Body.Bytes(), &resp)
	require.NoError(t, err)

	assert.Equal(t, "healthy", resp["status"])
	assert.Equal(t, Version, resp["version"])

	checks := resp["checks"].(map[string]interface{})
	assert.Equal(t, "ok", checks["postgres"])
	assert.Equal(t, "not configured", checks["redis"])
}

func TestReadinessCheck_DBDown_Returns503(t *testing.T) {
	// Open an in-memory SQLite DB, then close the underlying sql.DB to simulate failure
	db := setupTestDB(t)
	sqlDB, err := db.DB()
	require.NoError(t, err)
	sqlDB.Close()

	deps := &Dependencies{
		DB:    db,
		Cache: nil,
	}

	e := echo.New()
	e.GET("/api/health/", readinessCheck(deps))

	req := httptest.NewRequest(http.MethodGet, "/api/health/", nil)
	rec := httptest.NewRecorder()
	e.ServeHTTP(rec, req)

	assert.Equal(t, http.StatusServiceUnavailable, rec.Code)

	var resp map[string]interface{}
	err = json.Unmarshal(rec.Body.Bytes(), &resp)
	require.NoError(t, err)

	assert.Equal(t, "unhealthy", resp["status"])

	checks := resp["checks"].(map[string]interface{})
	assert.Contains(t, checks["postgres"], "ping failed")
}

func TestReadinessCheck_ResponseFormat(t *testing.T) {
	db := setupTestDB(t)

	deps := &Dependencies{
		DB:    db,
		Cache: nil,
	}

	e := echo.New()
	e.GET("/api/health/", readinessCheck(deps))

	req := httptest.NewRequest(http.MethodGet, "/api/health/", nil)
	rec := httptest.NewRecorder()
	e.ServeHTTP(rec, req)

	var resp map[string]interface{}
	err := json.Unmarshal(rec.Body.Bytes(), &resp)
	require.NoError(t, err)

	// Verify all expected fields are present
	assert.Contains(t, resp, "status")
	assert.Contains(t, resp, "version")
	assert.Contains(t, resp, "checks")
	assert.Contains(t, resp, "timestamp")

	// Verify checks is a map with expected keys
	checks, ok := resp["checks"].(map[string]interface{})
	require.True(t, ok, "checks should be a map")
	assert.Contains(t, checks, "postgres")
	assert.Contains(t, checks, "redis")
}
