package gobackend

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"
)

type LogEntry struct {
	Timestamp string `json:"timestamp"`
	Level     string `json:"level"`
	Tag       string `json:"tag"`
	Message   string `json:"message"`
}

type LogBuffer struct {
	entries        []LogEntry
	maxSize        int
	mu             sync.RWMutex
	loggingEnabled bool
}

var (
	globalLogBuffer *LogBuffer
	logBufferOnce   sync.Once
)

// GetLogBuffer returns the singleton log buffer instance
func GetLogBuffer() *LogBuffer {
	logBufferOnce.Do(func() {
		globalLogBuffer = &LogBuffer{
			entries:        make([]LogEntry, 0, 1000),
			maxSize:        1000,
			loggingEnabled: false, // Default: disabled for performance (user can enable in settings)
		}
	})
	return globalLogBuffer
}

func (lb *LogBuffer) SetLoggingEnabled(enabled bool) {
	lb.mu.Lock()
	defer lb.mu.Unlock()
	lb.loggingEnabled = enabled
}

// IsLoggingEnabled returns whether logging is enabled
func (lb *LogBuffer) IsLoggingEnabled() bool {
	lb.mu.RLock()
	defer lb.mu.RUnlock()
	return lb.loggingEnabled
}

func (lb *LogBuffer) Add(level, tag, message string) {
	lb.mu.Lock()
	defer lb.mu.Unlock()

	if !lb.loggingEnabled && level != "ERROR" && level != "FATAL" {
		return
	}

	entry := LogEntry{
		Timestamp: time.Now().Format("15:04:05.000"),
		Level:     level,
		Tag:       tag,
		Message:   message,
	}

	if len(lb.entries) >= lb.maxSize {
		lb.entries = lb.entries[1:]
	}
	lb.entries = append(lb.entries, entry)

	fmt.Printf("[%s] %s\n", tag, message)
}

// GetAll returns all log entries as JSON
func (lb *LogBuffer) GetAll() string {
	lb.mu.RLock()
	defer lb.mu.RUnlock()

	jsonBytes, _ := json.Marshal(lb.entries)
	return string(jsonBytes)
}

func (lb *LogBuffer) getSince(index int) ([]LogEntry, int) {
	lb.mu.RLock()
	defer lb.mu.RUnlock()

	if index < 0 {
		index = 0
	}
	if index >= len(lb.entries) {
		return []LogEntry{}, len(lb.entries)
	}

	entries := lb.entries[index:]
	return entries, len(lb.entries)
}

// Clear clears all log entries
func (lb *LogBuffer) Clear() {
	lb.mu.Lock()
	defer lb.mu.Unlock()
	lb.entries = lb.entries[:0]
}

// Count returns the number of log entries
func (lb *LogBuffer) Count() int {
	lb.mu.RLock()
	defer lb.mu.RUnlock()
	return len(lb.entries)
}

// Helper functions for logging with different levels
func LogDebug(tag, format string, args ...interface{}) {
	GetLogBuffer().Add("DEBUG", tag, fmt.Sprintf(format, args...))
}

func LogInfo(tag, format string, args ...interface{}) {
	GetLogBuffer().Add("INFO", tag, fmt.Sprintf(format, args...))
}

func LogWarn(tag, format string, args ...interface{}) {
	GetLogBuffer().Add("WARN", tag, fmt.Sprintf(format, args...))
}

func LogError(tag, format string, args ...interface{}) {
	GetLogBuffer().Add("ERROR", tag, fmt.Sprintf(format, args...))
}

// GoLog is a drop-in replacement for fmt.Printf that also logs to buffer
// It parses the tag from the format string if it starts with [Tag]
func GoLog(format string, args ...interface{}) {
	message := fmt.Sprintf(format, args...)
	message = strings.TrimSuffix(message, "\n")

	// Extract tag from message if present (e.g., "[Tidal] message")
	tag := "Go"
	level := "INFO"

	if strings.HasPrefix(message, "[") {
		endBracket := strings.Index(message, "]")
		if endBracket > 1 {
			tag = message[1:endBracket]
			message = strings.TrimSpace(message[endBracket+1:])
		}
	}

	// Determine level from message content
	msgLower := strings.ToLower(message)
	if strings.Contains(msgLower, "error") || strings.Contains(msgLower, "failed") {
		level = "ERROR"
	} else if strings.Contains(msgLower, "warning") || strings.Contains(msgLower, "warn") {
		level = "WARN"
	} else if strings.Contains(msgLower, "success") || strings.Contains(msgLower, "match found") {
		level = "INFO"
	} else if strings.Contains(msgLower, "searching") || strings.Contains(msgLower, "trying") || strings.Contains(msgLower, "found") {
		level = "DEBUG"
	}

	GetLogBuffer().Add(level, tag, message)
}

// Exported functions for Flutter

// GetLogs returns all logs as JSON array
func GetLogs() string {
	return GetLogBuffer().GetAll()
}

// GetLogsSince returns logs since the given index
// Returns JSON: {"logs": [...], "next_index": N}
func GetLogsSince(index int) string {
	entries, nextIndex := GetLogBuffer().getSince(index)
	logsJson, _ := json.Marshal(entries)
	result := fmt.Sprintf(`{"logs":%s,"next_index":%d}`, string(logsJson), nextIndex)
	return result
}

// ClearLogs clears all logs
func ClearLogs() {
	GetLogBuffer().Clear()
}

// GetLogCount returns the number of log entries
func GetLogCount() int {
	return GetLogBuffer().Count()
}

// SetLoggingEnabled enables or disables logging from Flutter
func SetLoggingEnabled(enabled bool) {
	GetLogBuffer().SetLoggingEnabled(enabled)
}
