import Foundation
import Logging

public enum CodexBarLog {
    public enum Destination: Sendable {
        case stderr
        case discard
        case oslog(subsystem: String)
    }

    public enum Level: String, CaseIterable, Identifiable, Sendable {
        case trace
        case verbose
        case debug
        case info
        case warning
        case error
        case critical

        public var id: String {
            self.rawValue
        }

        public var displayName: String {
            switch self {
            case .trace: "Trace"
            case .verbose: "Verbose"
            case .debug: "Debug"
            case .info: "Info"
            case .warning: "Warning"
            case .error: "Error"
            case .critical: "Critical"
            }
        }

        public var rank: Int {
            switch self {
            case .trace: 0
            case .verbose: 1
            case .debug: 2
            case .info: 3
            case .warning: 4
            case .error: 5
            case .critical: 6
            }
        }

        public var asSwiftLogLevel: Logger.Level {
            switch self {
            case .trace: .trace
            case .verbose: .debug
            case .debug: .debug
            case .info: .info
            case .warning: .warning
            case .error: .error
            case .critical: .critical
            }
        }
    }

    public struct Configuration: Sendable {
        public let destination: Destination
        public let level: Level
        public let json: Bool

        public init(destination: Destination, level: Level, json: Bool) {
            self.destination = destination
            self.level = level
            self.json = json
        }
    }

    private static let lock = NSLock()
    private static let levelLock = NSLock()
    private nonisolated(unsafe) static var isBootstrapped = false
    private nonisolated(unsafe) static var currentLevel: Level = .info

    public static func bootstrapIfNeeded(_ config: Configuration) {
        self.lock.lock()
        defer { lock.unlock() }
        guard !self.isBootstrapped else { return }
        self.currentLevel = config.level

        let baseFactory: @Sendable (String) -> any LogHandler = { label in
            switch config.destination {
            case .stderr:
                if config.json { return JSONStderrLogHandler(label: label) }
                return StreamLogHandler.standardError(label: label)
            case .discard:
                return DiscardLogHandler()
            case let .oslog(subsystem):
                #if canImport(os)
                return OSLogLogHandler(label: label, subsystem: subsystem)
                #else
                if config.json { return JSONStderrLogHandler(label: label) }
                return StreamLogHandler.standardError(label: label)
                #endif
            }
        }

        LoggingSystem.bootstrap { label in
            let primary = baseFactory(label)
            let fileHandler = FileLogHandler(label: label)
            var handler = CompositeLogHandler(primary: primary, secondary: fileHandler)
            handler.logLevel = .trace
            return handler
        }

        self.isBootstrapped = true
    }

    public static func logger(_ category: String) -> CodexBarLogger {
        let logger = Logger(label: "com.steipete.codexbar.\(category)")
        return CodexBarLogger(isEnabled: { level in
            self.shouldLog(level)
        }, log: { level, message, metadata in
            let swiftLogLevel = level.asSwiftLogLevel
            let safeMessage = LogRedactor.redact(message)
            let meta = metadata?.reduce(into: Logger.Metadata()) { partial, entry in
                partial[entry.key] = .string(LogRedactor.redact(entry.value))
            }
            logger.log(level: swiftLogLevel, "\(safeMessage)", metadata: meta)
        })
    }

    public static func parseLevel(_ raw: String?) -> Level? {
        guard let raw, !raw.isEmpty else { return nil }
        return Level(rawValue: raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased())
    }

    public static func setLogLevel(_ level: Level) {
        self.levelLock.lock()
        self.currentLevel = level
        self.levelLock.unlock()
        let logger = self.logger(LogCategories.logging)
        logger.info("Log level set to \(level.rawValue)")
    }

    public static func currentLogLevel() -> Level {
        self.levelLock.lock()
        defer { self.levelLock.unlock() }
        return self.currentLevel
    }

    private static func shouldLog(_ level: Level) -> Bool {
        level.rank >= self.currentLogLevel().rank
    }

    public static var fileLogURL: URL {
        FileLogSink.defaultURL
    }

    public static func setFileLoggingEnabled(_ enabled: Bool) {
        FileLogSink.shared.setEnabled(enabled, fileURL: self.fileLogURL)
        let state = enabled ? "enabled" : "disabled"
        let logger = self.logger(LogCategories.logging)
        logger.info("File logging \(state)", metadata: ["path": self.fileLogURL.path])
    }
}

private struct DiscardLogHandler: LogHandler {
    var metadata: Logger.Metadata = [:]
    var logLevel: Logger.Level = .critical

    subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
        get { self.metadata[metadataKey] }
        set { self.metadata[metadataKey] = newValue }
    }

    func log(event _: LogEvent) {}
}

public struct CodexBarLogger: Sendable {
    private let isEnabled: @Sendable (CodexBarLog.Level) -> Bool
    private let logFn: @Sendable (CodexBarLog.Level, String, [String: String]?) -> Void

    fileprivate init(
        isEnabled: @escaping @Sendable (CodexBarLog.Level) -> Bool,
        log: @escaping @Sendable (CodexBarLog.Level, String, [String: String]?) -> Void)
    {
        self.isEnabled = isEnabled
        self.logFn = log
    }

    init(
        minimumLevel: CodexBarLog.Level,
        log: @escaping @Sendable (CodexBarLog.Level, String, [String: String]?) -> Void)
    {
        self.isEnabled = { level in level.rank >= minimumLevel.rank }
        self.logFn = log
    }

    private func log(
        _ level: CodexBarLog.Level,
        _ message: @autoclosure () -> String,
        metadata: [String: String]?)
    {
        guard self.isEnabled(level) else { return }
        self.logFn(level, message(), metadata)
    }

    public func trace(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.trace, message(), metadata: metadata)
    }

    public func verbose(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.verbose, message(), metadata: metadata)
    }

    public func debug(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.debug, message(), metadata: metadata)
    }

    public func info(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.info, message(), metadata: metadata)
    }

    public func warning(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.warning, message(), metadata: metadata)
    }

    public func error(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.error, message(), metadata: metadata)
    }

    public func critical(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {
        self.log(.critical, message(), metadata: metadata)
    }
}
