package audit

import (
	"bufio"
	"encoding/json"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestLogger_Integration(t *testing.T) {
	// Create temp file
	tmpDir := t.TempDir()
	tmpFile := filepath.Join(tmpDir, "audit.log")

	// Create logger
	logger, err := NewLogger(&Config{
		Enabled:       true,
		FilePath:      tmpFile,
		BufferSize:    1024,
		ChannelSize:   10,
		FlushInterval: 100 * time.Millisecond,
	})
	require.NoError(t, err)

	// Log entries
	entries := []*Entry{
		{
			Timestamp:      time.Now().UTC().Format(time.RFC3339Nano),
			RequestMethod:  "GET",
			RequestURI:     "/api/v1/clusters",
			ResponseStatus: 200,
			LatencyMs:      50,
			ClientIP:       "192.168.1.1",
		},
		{
			Timestamp:      time.Now().UTC().Format(time.RFC3339Nano),
			RequestMethod:  "POST",
			RequestURI:     "/api/v1/clusters/create",
			ResponseStatus: 201,
			LatencyMs:      100,
			ClientIP:       "192.168.1.2",
		},
	}

	for _, entry := range entries {
		logger.Log(entry)
	}

	// Close logger to ensure all data is flushed
	err = logger.Close()
	require.NoError(t, err)

	// Read and verify file content
	file, err := os.Open(tmpFile)
	require.NoError(t, err)
	defer file.Close()

	scanner := bufio.NewScanner(file)
	var readEntries []*Entry
	for scanner.Scan() {
		var entry Entry
		err := json.Unmarshal(scanner.Bytes(), &entry)
		require.NoError(t, err)
		readEntries = append(readEntries, &entry)
	}
	require.NoError(t, scanner.Err())

	// Verify entries
	require.Len(t, readEntries, 2)
	assert.Equal(t, "GET", readEntries[0].RequestMethod)
	assert.Equal(t, "/api/v1/clusters", readEntries[0].RequestURI)
	assert.Equal(t, 200, readEntries[0].ResponseStatus)
	assert.Equal(t, "POST", readEntries[1].RequestMethod)
}

func TestLogger_GracefulShutdown(t *testing.T) {
	tmpDir := t.TempDir()
	tmpFile := filepath.Join(tmpDir, "audit.log")

	logger, err := NewLogger(&Config{
		Enabled:       true,
		FilePath:      tmpFile,
		BufferSize:    4096,
		ChannelSize:   100,
		FlushInterval: 10 * time.Second, // Long interval to test immediate flush on close
	})
	require.NoError(t, err)

	// Log many entries quickly
	for i := 0; i < 50; i++ {
		logger.Log(&Entry{
			Timestamp:      time.Now().UTC().Format(time.RFC3339Nano),
			RequestMethod:  "GET",
			RequestURI:     "/api/test",
			ResponseStatus: 200,
			LatencyMs:      int64(i),
		})
	}

	// Close immediately without waiting for flush interval
	err = logger.Close()
	require.NoError(t, err)

	// Verify all entries were written
	data, err := os.ReadFile(tmpFile)
	require.NoError(t, err)

	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	assert.Len(t, lines, 50, "all 50 entries should be persisted on shutdown")
}

func TestLogger_DoubleClose(t *testing.T) {
	tmpDir := t.TempDir()
	tmpFile := filepath.Join(tmpDir, "audit.log")

	logger, err := NewLogger(&Config{
		Enabled:  true,
		FilePath: tmpFile,
	})
	require.NoError(t, err)

	// First close should succeed
	err = logger.Close()
	require.NoError(t, err)

	// Second close should be no-op and not error
	err = logger.Close()
	require.NoError(t, err)
}

func TestLogger_LogAfterClose(t *testing.T) {
	tmpDir := t.TempDir()
	tmpFile := filepath.Join(tmpDir, "audit.log")

	logger, err := NewLogger(&Config{
		Enabled:  true,
		FilePath: tmpFile,
	})
	require.NoError(t, err)

	// Log one entry
	logger.Log(&Entry{
		Timestamp:     time.Now().UTC().Format(time.RFC3339Nano),
		RequestMethod: "GET",
		RequestURI:    "/before-close",
	})

	// Close logger
	err = logger.Close()
	require.NoError(t, err)

	// This should not panic - the done channel signals Log() to return early
	logger.Log(&Entry{
		Timestamp:     time.Now().UTC().Format(time.RFC3339Nano),
		RequestMethod: "GET",
		RequestURI:    "/after-close",
	})

	// Verify only the entry before close was written
	data, err := os.ReadFile(tmpFile)
	require.NoError(t, err)

	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	assert.Len(t, lines, 1)
	assert.Contains(t, lines[0], "/before-close")
}

func TestLogger_LogDuringShutdown(t *testing.T) {
	// This test verifies that Log() handles shutdown gracefully without panic.
	// The done channel pattern ensures safe concurrent access during shutdown.
	tmpDir := t.TempDir()
	tmpFile := filepath.Join(tmpDir, "audit.log")

	logger, err := NewLogger(&Config{
		Enabled:  true,
		FilePath: tmpFile,
	})
	require.NoError(t, err)

	// Close the logger
	err = logger.Close()
	require.NoError(t, err)

	// Multiple Log() calls after Close() should not panic
	assert.NotPanics(t, func() {
		for i := 0; i < 10; i++ {
			logger.Log(&Entry{
				Timestamp:     time.Now().UTC().Format(time.RFC3339Nano),
				RequestMethod: "GET",
				RequestURI:    "/test",
			})
		}
	}, "Log() after Close() should not panic")
}
