# SPDX-License-Identifier: LSL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus Sovereign Core.
# See legal/LICENSE_SOVEREIGN.md for license terms.

## logger.nim
## Logging system for NIP MVP

import std/[times, strformat, os, strutils]

type
  LogLevel* = enum
    Debug, Info, Warning, Error, Fatal

  Logger* = ref object
    logFile*: string
    minLevel*: LogLevel
    enabled*: bool
    verbose*: bool

var globalLogger*: Logger = nil

proc newLogger*(logFile: string = "/var/log/nip.log",
                minLevel: LogLevel = Info,
                verbose: bool = false): Logger =
  ## Create a new logger
  result = Logger(
    logFile: logFile,
    minLevel: minLevel,
    enabled: true,
    verbose: verbose
  )

proc initGlobalLogger*(logFile: string = "/var/log/nip.log",
                       minLevel: LogLevel = Info,
                       verbose: bool = false) =
  ## Initialize the global logger
  globalLogger = newLogger(logFile, minLevel, verbose)

proc levelToString(level: LogLevel): string =
  case level
  of Debug: "DEBUG"
  of Info: "INFO"
  of Warning: "WARN"
  of Error: "ERROR"
  of Fatal: "FATAL"

proc log*(logger: Logger, level: LogLevel, message: string) =
  ## Log a message
  if not logger.enabled:
    return

  if level < logger.minLevel:
    return

  let timestamp = now().format("yyyy-MM-dd HH:mm:ss")
  let levelStr = levelToString(level)
  let logLine = fmt"[{timestamp}] [{levelStr}] {message}"

  # Print to console if verbose or error/fatal
  if logger.verbose or level >= Error:
    echo logLine

  # Write to log file
  try:
    let logDir = parentDir(logger.logFile)
    if not dirExists(logDir):
      try:
        createDir(logDir)
      except:
        # Can't create log directory, skip file logging
        return

    let file = open(logger.logFile, fmAppend)
    file.writeLine(logLine)
    file.close()
  except IOError, OSError:
    # Can't write to log file, just skip it
    discard

# Convenience functions for global logger
proc logDebug*(message: string) =
  if globalLogger != nil:
    globalLogger.log(Debug, message)

proc logInfo*(message: string) =
  if globalLogger != nil:
    globalLogger.log(Info, message)

proc logWarning*(message: string) =
  if globalLogger != nil:
    globalLogger.log(Warning, message)

proc logError*(message: string) =
  if globalLogger != nil:
    globalLogger.log(Error, message)

proc logFatal*(message: string) =
  if globalLogger != nil:
    globalLogger.log(Fatal, message)

# Operation logging helpers
proc logOperation*(operation: string, details: string = "") =
  let msg = if details != "": fmt"{operation}: {details}" else: operation
  logInfo(msg)

proc logSuccess*(operation: string, details: string = "") =
  let msg = if details != "": fmt"✅ {operation}: {details}" else: fmt"✅ {operation}"
  logInfo(msg)

proc logFailure*(operation: string, error: string) =
  logError(fmt"❌ {operation} failed: {error}")

proc logException*(operation: string, e: ref Exception) =
  logError(fmt"Exception in {operation}: {e.msg}")
  if globalLogger != nil and globalLogger.verbose:
    logError(e.getStackTrace())

# Log rotation
proc rotateLog*(logger: Logger, maxSize: int64 = 10_000_000) =
  ## Rotate log file if it exceeds maxSize (default 10MB)
  if not fileExists(logger.logFile):
    return

  try:
    let size = getFileSize(logger.logFile)
    if size > maxSize:
      # Rotate: nip.log -> nip.log.1, nip.log.1 -> nip.log.2, etc.
      for i in countdown(2, 1):
        let oldFile = logger.logFile & "." & $i
        let newFile = logger.logFile & "." & $(i + 1)
        if fileExists(oldFile):
          moveFile(oldFile, newFile)

      # Move current log to .1
      moveFile(logger.logFile, logger.logFile & ".1")

      logInfo("Log file rotated")
  except:
    discard

proc getLogPath*(): string =
  ## Get the current log file path
  if globalLogger != nil:
    return globalLogger.logFile
  else:
    return "/var/log/nip.log"

proc setVerbose*(verbose: bool) =
  ## Set verbose mode for global logger
  if globalLogger != nil:
    globalLogger.verbose = verbose
