package services

import (
	"encoding/json"
	"os"
	"path/filepath"
	"testing"

	"github.com/Wikid82/charon/backend/internal/config"
	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestLogService(t *testing.T) {
	tmpDir, err := os.MkdirTemp("", "cpm-log-service-test")
	require.NoError(t, err)
	defer os.RemoveAll(tmpDir)

	dataDir := filepath.Join(tmpDir, "data")
	logsDir := filepath.Join(dataDir, "logs")
	err = os.MkdirAll(logsDir, 0o755)
	require.NoError(t, err)

	// Create sample JSON logs
	logEntry1 := models.CaddyAccessLog{
		Level:  "info",
		Ts:     1600000000,
		Msg:    "request handled",
		Status: 200,
	}
	logEntry1.Request.Method = "GET"
	logEntry1.Request.Host = "example.com"
	logEntry1.Request.URI = "/"
	logEntry1.Request.RemoteIP = "1.2.3.4"

	logEntry2 := models.CaddyAccessLog{
		Level:  "error",
		Ts:     1600000060,
		Msg:    "error handled",
		Status: 500,
	}
	logEntry2.Request.Method = "POST"
	logEntry2.Request.Host = "api.example.com"
	logEntry2.Request.URI = "/submit"
	logEntry2.Request.RemoteIP = "5.6.7.8"

	line1, _ := json.Marshal(logEntry1)
	line2, _ := json.Marshal(logEntry2)

	content := string(line1) + "\n" + string(line2) + "\n"

	err = os.WriteFile(filepath.Join(logsDir, "access.log"), []byte(content), 0o644)
	require.NoError(t, err)
	err = os.WriteFile(filepath.Join(logsDir, "other.txt"), []byte("ignore me"), 0o644)
	require.NoError(t, err)

	cfg := &config.Config{DatabasePath: filepath.Join(dataDir, "charon.db")}
	service := NewLogService(cfg)

	// Test List
	logs, err := service.ListLogs()
	require.NoError(t, err)
	assert.Len(t, logs, 1)
	assert.Equal(t, "access.log", logs[0].Name)

	// Test QueryLogs - All
	results, total, err := service.QueryLogs("access.log", models.LogFilter{Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(2), total)
	assert.Len(t, results, 2)
	// Should be reversed (newest first)
	assert.Equal(t, 500, results[0].Status)
	assert.Equal(t, 200, results[1].Status)

	// Test QueryLogs - Filter Status
	results, total, err = service.QueryLogs("access.log", models.LogFilter{Status: "5xx", Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(1), total)
	assert.Len(t, results, 1)
	assert.Equal(t, 500, results[0].Status)

	// Test QueryLogs - Filter Host
	results, total, err = service.QueryLogs("access.log", models.LogFilter{Host: "api.example.com", Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(1), total)
	assert.Len(t, results, 1)
	assert.Equal(t, "api.example.com", results[0].Request.Host)

	// Test QueryLogs - Search
	results, total, err = service.QueryLogs("access.log", models.LogFilter{Search: "submit", Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(1), total)
	assert.Len(t, results, 1)
	assert.Equal(t, "/submit", results[0].Request.URI)

	// Test GetLogPath
	path, err := service.GetLogPath("access.log")
	require.NoError(t, err)
	assert.Equal(t, filepath.Join(logsDir, "access.log"), path)

	// Test GetLogPath non-existent
	_, err = service.GetLogPath("missing.log")
	assert.Error(t, err)

	// Test GetLogPath - Invalid
	_, err = service.GetLogPath("nonexistent.log")
	assert.Error(t, err)

	// Test GetLogPath - Traversal
	_, err = service.GetLogPath("../../etc/passwd")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "invalid filename")

	// Test ListLogs - Directory Not Exist
	nonExistService := NewLogService(&config.Config{DatabasePath: filepath.Join(t.TempDir(), "missing", "charon.db")})
	logs, err = nonExistService.ListLogs()
	require.NoError(t, err)
	assert.Empty(t, logs)

	// Test QueryLogs - Non-JSON Logs
	plainContent := "2023/10/27 10:00:00 Application started\nJust a plain line\n"
	err = os.WriteFile(filepath.Join(logsDir, "app.log"), []byte(plainContent), 0o644)
	require.NoError(t, err)

	results, total, err = service.QueryLogs("app.log", models.LogFilter{Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(2), total)
	// Reverse order check
	assert.Equal(t, "Just a plain line", results[0].Msg)
	assert.Equal(t, "Application started", results[1].Msg)
	assert.Equal(t, "INFO", results[1].Level)

	// Test QueryLogs - Pagination
	// We have 2 logs in access.log
	results, _, err = service.QueryLogs("access.log", models.LogFilter{Limit: 1, Offset: 0})
	require.NoError(t, err)
	assert.Len(t, results, 1)
	assert.Equal(t, 500, results[0].Status) // Newest first

	results, _, err = service.QueryLogs("access.log", models.LogFilter{Limit: 1, Offset: 1})
	require.NoError(t, err)
	assert.Len(t, results, 1)
	assert.Equal(t, 200, results[0].Status) // Second newest

	results, _, err = service.QueryLogs("access.log", models.LogFilter{Limit: 10, Offset: 5})
	require.NoError(t, err)
	assert.Empty(t, results)

	// Test QueryLogs - Exact Status Match
	results, total, err = service.QueryLogs("access.log", models.LogFilter{Status: "200", Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(1), total)
	assert.Equal(t, 200, results[0].Status)

	// Test QueryLogs - Search Fields
	// Search Method
	results, total, err = service.QueryLogs("access.log", models.LogFilter{Search: "POST", Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(1), total)
	assert.Equal(t, "POST", results[0].Request.Method)

	// Search RemoteIP
	results, total, err = service.QueryLogs("access.log", models.LogFilter{Search: "5.6.7.8", Limit: 10})
	require.NoError(t, err)
	assert.Equal(t, int64(1), total)
	assert.Equal(t, "5.6.7.8", results[0].Request.RemoteIP)
}
