package gobackend

import (
	"encoding/json"
	"fmt"
	"regexp"
	"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
	head           int
	count          int
	nextIndex      int
	mu             sync.RWMutex
	loggingEnabled bool
}

const (
	defaultLogBufferSize = 500
)

var (
	globalLogBuffer *LogBuffer
	logBufferOnce   sync.Once

	authorizationBearerPattern = regexp.MustCompile(`(?i)\bAuthorization\b\s*[:=]\s*Bearer\s+[A-Za-z0-9._~+/\-]+=*`)
	genericKeyValuePattern     = regexp.MustCompile(`(?i)\b(access[_\s-]?token|refresh[_\s-]?token|id[_\s-]?token|client[_\s-]?secret|authorization|password|api[_\s-]?key)\b(\s*[:=]\s*)([^\s,;]+)`)
	queryTokenPattern          = regexp.MustCompile(`(?i)([?&](?:access_token|refresh_token|id_token|token|client_secret|api_key|apikey|password)=)[^&\s]+`)
	bearerTokenPattern         = regexp.MustCompile(`(?i)\bBearer\s+[A-Za-z0-9._~+/\-]+=*`)
)

func sanitizeSensitiveLogText(message string) string {
	redacted := message
	redacted = authorizationBearerPattern.ReplaceAllString(redacted, "Authorization: Bearer [REDACTED]")
	redacted = genericKeyValuePattern.ReplaceAllString(redacted, `${1}${2}[REDACTED]`)
	redacted = queryTokenPattern.ReplaceAllString(redacted, `${1}[REDACTED]`)
	redacted = bearerTokenPattern.ReplaceAllString(redacted, "Bearer [REDACTED]")
	return redacted
}

func GetLogBuffer() *LogBuffer {
	logBufferOnce.Do(func() {
		globalLogBuffer = &LogBuffer{
			entries:        make([]LogEntry, defaultLogBufferSize),
			maxSize:        defaultLogBufferSize,
			loggingEnabled: false,
		}
	})
	return globalLogBuffer
}

func (lb *LogBuffer) SetLoggingEnabled(enabled bool) {
	lb.mu.Lock()
	defer lb.mu.Unlock()
	lb.loggingEnabled = 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
	}

	message = sanitizeSensitiveLogText(message)

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

	if lb.maxSize <= 0 {
		return
	}
	if len(lb.entries) != lb.maxSize {
		lb.entries = make([]LogEntry, lb.maxSize)
		lb.head = 0
		lb.count = 0
	}
	if lb.count < lb.maxSize {
		index := (lb.head + lb.count) % lb.maxSize
		lb.entries[index] = entry
		lb.count++
	} else {
		lb.entries[lb.head] = entry
		lb.head = (lb.head + 1) % lb.maxSize
	}
	lb.nextIndex++

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

func (lb *LogBuffer) GetAll() string {
	lb.mu.RLock()
	defer lb.mu.RUnlock()

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

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

	earliest := lb.nextIndex - lb.count
	if index < earliest {
		index = earliest
	}
	if index >= lb.nextIndex {
		return []LogEntry{}, lb.nextIndex
	}
	return lb.snapshotLocked(index - earliest), lb.nextIndex
}

func (lb *LogBuffer) snapshotLocked(offset int) []LogEntry {
	if offset < 0 {
		offset = 0
	}
	if offset >= lb.count {
		return []LogEntry{}
	}
	result := make([]LogEntry, lb.count-offset)
	for i := offset; i < lb.count; i++ {
		result[i-offset] = lb.entries[(lb.head+i)%lb.maxSize]
	}
	return result
}

func (lb *LogBuffer) Clear() {
	lb.mu.Lock()
	defer lb.mu.Unlock()
	for i := range lb.entries {
		lb.entries[i] = LogEntry{}
	}
	lb.head = 0
	lb.count = 0
}

func (lb *LogBuffer) Count() int {
	lb.mu.RLock()
	defer lb.mu.RUnlock()
	return lb.count
}

func LogDebug(tag, format string, args ...any) {
	GetLogBuffer().Add("DEBUG", tag, fmt.Sprintf(format, args...))
}

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

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

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

func GoLog(format string, args ...any) {
	message := fmt.Sprintf(format, args...)
	message = strings.TrimSuffix(message, "\n")

	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:])
		}
	}

	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)
}

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
}

func ClearLogs() {
	GetLogBuffer().Clear()
}

func SetLoggingEnabled(enabled bool) {
	GetLogBuffer().SetLoggingEnabled(enabled)
}
