//
//  Logging.swift
//  Confab
//
//  Dual logging: os.Logger (unified log) + file (~Library/Logs/oAI.log)
//
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Copyright (C) 2026 Rune Olsen
//
// This file is part of Confab.
//
// Confab is licensed under the PolyForm Noncommercial License 1.0.0.
// You may use, study, modify, and share it for any noncommercial
// purpose. Commercial use — including selling Confab or any part of
// it, standalone or bundled into another product or service —
// requires a separate commercial license from the copyright holder.
//
// See the LICENSE file or
// <https://polyformproject.org/licenses/noncommercial/1.0.0> for
// the full license text. For commercial licensing, contact Rune
// Olsen via <https://confab.no>.


import Foundation
import os

// MARK: - Log Level

enum LogLevel: Int, Comparable, CaseIterable, Sendable {
    case debug = 0
    case info = 1
    case warning = 2
    case error = 3

    var label: String {
        switch self {
        case .debug: return "DEBUG"
        case .info: return "INFO"
        case .warning: return "WARN"
        case .error: return "ERROR"
        }
    }

    var displayName: String {
        switch self {
        case .debug: return "Debug"
        case .info: return "Info"
        case .warning: return "Warning"
        case .error: return "Error"
        }
    }

    nonisolated static func < (lhs: LogLevel, rhs: LogLevel) -> Bool {
        lhs.rawValue < rhs.rawValue
    }
}

// MARK: - File Logger

final class FileLogger: @unchecked Sendable {
    nonisolated static let shared = FileLogger()

    private let fileHandle: FileHandle?
    private let queue = DispatchQueue(label: "com.oai.filelogger")
    private let dateFormatter: DateFormatter = {
        let f = DateFormatter()
        f.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        return f
    }()

    /// Current minimum log level (backed by UserDefaults — thread-safe).
    nonisolated var minimumLevel: LogLevel {
        get {
            let raw = UserDefaults.standard.integer(forKey: "logLevel")
            return LogLevel(rawValue: raw) ?? .info
        }
        set {
            UserDefaults.standard.set(newValue.rawValue, forKey: "logLevel")
        }
    }

    private init() {
        let logsDir = FileManager.default.homeDirectoryForCurrentUser
            .appendingPathComponent("Library/Logs")
        let logFile = logsDir.appendingPathComponent("Confab.log")

        // Ensure file exists
        if !FileManager.default.fileExists(atPath: logFile.path) {
            FileManager.default.createFile(atPath: logFile.path, contents: nil)
        }

        fileHandle = try? FileHandle(forWritingTo: logFile)
        fileHandle?.seekToEndOfFile()
    }

    nonisolated func write(_ level: LogLevel, category: String, message: String) {
        guard level >= minimumLevel else { return }
        queue.async { [weak self] in
            guard let self, let fh = self.fileHandle else { return }
            let timestamp = self.dateFormatter.string(from: Date())
            let line = "[\(timestamp)] [\(level.label)] [\(category)] \(message)\n"
            if let data = line.data(using: .utf8) {
                fh.write(data)
            }
        }
    }

    deinit {
        fileHandle?.closeFile()
    }
}

// MARK: - App Logger (wraps os.Logger + file)

// os.Logger methods are @MainActor in macOS 27. AppLogger is Sendable and all methods are
// nonisolated — FileLogger runs on its own serial queue, os.Logger dispatches to main actor.
struct AppLogger: Sendable {
    let subsystem: String
    let category: String

    nonisolated func debug(_ message: String) {
        FileLogger.shared.write(.debug, category: category, message: message)
        Task { @MainActor [self] in Logger(subsystem: subsystem, category: category).debug("\(message, privacy: .public)") }
    }

    nonisolated func info(_ message: String) {
        FileLogger.shared.write(.info, category: category, message: message)
        Task { @MainActor [self] in Logger(subsystem: subsystem, category: category).info("\(message, privacy: .public)") }
    }

    nonisolated func warning(_ message: String) {
        FileLogger.shared.write(.warning, category: category, message: message)
        Task { @MainActor [self] in Logger(subsystem: subsystem, category: category).warning("\(message, privacy: .public)") }
    }

    nonisolated func error(_ message: String) {
        FileLogger.shared.write(.error, category: category, message: message)
        Task { @MainActor [self] in Logger(subsystem: subsystem, category: category).error("\(message, privacy: .public)") }
    }
}

// MARK: - Log Namespace

enum Log {
    nonisolated static let subsystem = "com.oai.Confab"

    nonisolated static let api      = AppLogger(subsystem: subsystem, category: "api")
    nonisolated static let db       = AppLogger(subsystem: subsystem, category: "database")
    nonisolated static let mcp      = AppLogger(subsystem: subsystem, category: "mcp")
    nonisolated static let settings = AppLogger(subsystem: subsystem, category: "settings")
    nonisolated static let search   = AppLogger(subsystem: subsystem, category: "search")
    nonisolated static let ui       = AppLogger(subsystem: subsystem, category: "ui")
    nonisolated static let general  = AppLogger(subsystem: subsystem, category: "general")
    nonisolated static let extMcp   = AppLogger(subsystem: subsystem, category: "ext-mcp")
    nonisolated static let cli      = AppLogger(subsystem: subsystem, category: "cli")
}
