package lx

import (
	"strings"
	"time"
)

// Formatting constants for log output.
// These constants define the characters used to format log messages, ensuring consistency
// across handlers (e.g., text, JSON, colorized). They are used to construct namespace paths,
// level indicators, and field separators in log entries.
const (
	Space        = " "  // Single space for separating elements (e.g., between level and message)
	DoubleSpace  = "  " // Double space for indentation (e.g., for hierarchical output)
	Slash        = "/"  // Separator for namespace paths (e.g., "parent/child")
	Arrow        = "→"  // Arrow for NestedPath style namespaces (e.g., [parent]→[child])
	LeftBracket  = "["  // Opening bracket for namespaces and fields (e.g., [app])
	RightBracket = "]"  // Closing bracket for namespaces and fields (e.g., [app])
	Colon        = ":"  // Separator after namespace or level (e.g., [app]: INFO:)
	Dot          = "."  // Separator for namespace paths (e.g., "parent.child")
	Newline      = "\n" // Newline for separating log entries or stack trace lines
)

// DefaultEnabled defines the default logging state (disabled).
// It specifies whether logging is enabled by default for new Logger instances in the ll package.
// Set to false to prevent logging until explicitly enabled.
const (
	DefaultEnabled = false // Default state for new loggers (disabled)
)

// Log level constants, ordered by increasing severity.
// These constants define the severity levels for log messages, used to filter logs based
// on the logger’s minimum level. They are ordered to allow comparison (e.g., LevelDebug < LevelWarn).
const (
	LevelNone    LevelType = iota // Debug level for detailed diagnostic information
	LevelInfo                     // Info level for general operational messages
	LevelWarn                     // Warn level for warning conditions
	LevelError                    // Error level for error conditions requiring attention
	LevelDebug                    // None level for logs without a specific severity (e.g., raw output)
	LevelUnknown                  // None level for logs without a specific severity (e.g., raw output)
)

// String constants for each level
const (
	DebugString   = "DEBUG"
	InfoString    = "INFO"
	WarnString    = "WARN"
	ErrorString   = "ERROR"
	NoneString    = "NONE"
	UnknownString = "UNKNOWN"

	TextString    = "TEXT"
	JSONString    = "JSON"
	DumpString    = "DUMP"
	SpecialString = "SPECIAL"
	RawString     = "RAW"
)

// Log class constants, defining the type of log entry.
// These constants categorize log entries by their content or purpose, influencing how
// handlers process them (e.g., text, JSON, hex dump).
const (
	ClassText    ClassType = iota // Text entries for standard log messages
	ClassJSON                     // JSON entries for structured output
	ClassDump                     // Dump entries for hex/ASCII dumps
	ClassSpecial                  // Special entries for custom or non-standard logs
	ClassRaw                      // Raw entries for unformatted output
	ClassUnknown                  // Raw entries for unformatted output
)

// Namespace style constants.
// These constants define how namespace paths are formatted in log output, affecting the
// visual representation of hierarchical namespaces.
const (
	FlatPath   StyleType = iota // Formats namespaces as [parent/child]
	NestedPath                  // Formats namespaces as [parent]→[child]
)

// LevelType represents the severity of a log message.
// It is an integer type used to define log levels (Debug, Info, Warn, Error, None), with associated
// string representations for display in log output.
type LevelType int

// String converts a LevelType to its string representation.
// It maps each level constant to a human-readable string, returning "UNKNOWN" for invalid levels.
// Used by handlers to display the log level in output.
// Example:
//
//	var level lx.LevelType = lx.LevelInfo
//	fmt.Println(level.String()) // Output: INFO
func (l LevelType) String() string {
	switch l {
	case LevelDebug:
		return DebugString
	case LevelInfo:
		return InfoString
	case LevelWarn:
		return WarnString
	case LevelError:
		return ErrorString
	case LevelNone:
		return NoneString
	default:
		return UnknownString
	}
}

// LevelParse converts a string to its corresponding LevelType.
// It parses a string (case-insensitive) and returns the corresponding LevelType, defaulting to
// LevelUnknown for unrecognized strings. Supports "WARNING" as an alias for "WARN".
func LevelParse(s string) LevelType {
	switch strings.ToUpper(s) {
	case DebugString:
		return LevelDebug
	case InfoString:
		return LevelInfo
	case WarnString, "WARNING": // Allow both "WARN" and "WARNING"
		return LevelWarn
	case ErrorString:
		return LevelError
	case NoneString:
		return LevelNone
	default:
		return LevelUnknown
	}
}

// StyleType defines how namespace paths are formatted in log output.
// It is an integer type used to select between FlatPath ([parent/child]) and NestedPath
// ([parent]→[child]) styles, affecting how handlers render namespace hierarchies.
type StyleType int

// Entry represents a single log entry passed to handlers.
// It encapsulates all information about a log message, including its timestamp, severity,
// content, namespace, metadata, and formatting style. Handlers process Entry instances
// to produce formatted output (e.g., text, JSON). The struct is immutable once created,
// ensuring thread-safety in handler processing.
type Entry struct {
	Timestamp time.Time              // Time the log was created
	Level     LevelType              // Severity level of the log (Debug, Info, Warn, Error, None)
	Message   string                 // Log message content
	Namespace string                 // Namespace path (e.g., "parent/child")
	Fields    map[string]interface{} // Additional key-value metadata (e.g., {"user": "alice"})
	Style     StyleType              // Namespace formatting style (FlatPath or NestedPath)
	Error     error                  // Associated error, if any (e.g., for error logs)
	Class     ClassType              // Type of log entry (Text, JSON, Dump, Special, Raw)
	Stack     []byte                 // Stack trace data (if present)
	Id        int                    `json:"-"` // Unique ID for the entry, ignored in JSON output
}

// Handler defines the interface for processing log entries.
// Implementations (e.g., TextHandler, JSONHandler) format and output log entries to various
// destinations (e.g., stdout, files). The Handle method returns an error if processing fails,
// allowing the logger to handle output failures gracefully.
// Example (simplified handler implementation):
//
//	type MyHandler struct{}
//	func (h *MyHandler) Handle(e *Entry) error {
//	    fmt.Printf("[%s] %s: %s\n", e.Namespace, e.Level.String(), e.Message)
//	    return nil
//	}
type Handler interface {
	Handle(e *Entry) error // Processes a log entry, returning any error
}

// Timestamper defines an interface for handlers that support timestamp configuration.
// It includes a method to enable or disable timestamp logging and optionally set the timestamp format.
type Timestamper interface {
	// Timestamped enables or disables timestamp logging and allows specifying an optional format.
	// Parameters:
	//   enable: Boolean to enable or disable timestamp logging
	//   format: Optional string(s) to specify the timestamp format
	Timestamped(enable bool, format ...string)
}

// ClassType represents the type of a log entry.
// It is an integer type used to categorize log entries (Text, JSON, Dump, Special, Raw),
// influencing how handlers process and format them.
type ClassType int

// String converts a ClassType to its string representation.
// It maps each class constant to a human-readable string, returning "UNKNOWN" for invalid classes.
// Used by handlers to indicate the entry type in output (e.g., JSON fields).
// Example:
//
//	var class lx.ClassType = lx.ClassText
//	fmt.Println(class.String()) // Output: TEST
func (t ClassType) String() string {
	switch t {
	case ClassText:
		return TextString
	case ClassJSON:
		return JSONString
	case ClassDump:
		return DumpString
	case ClassSpecial:
		return SpecialString
	case ClassRaw:
		return RawString
	default:
		return UnknownString
	}
}

// ParseClass converts a string to its corresponding ClassType.
// It parses a string (case-insensitive) and returns the corresponding ClassType, defaulting to
// ClassUnknown for unrecognized strings.
func ParseClass(s string) ClassType {
	switch strings.ToUpper(s) {
	case TextString:
		return ClassText
	case JSONString:
		return ClassJSON
	case DumpString:
		return ClassDump
	case SpecialString:
		return ClassSpecial
	case RawString:
		return ClassRaw
	default:
		return ClassUnknown
	}
}
