//
//  Logging.swift
//  oAI
//
//  Dual logging: os.Logger (unified log) + file (~Library/Logs/oAI.log)
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Rune Olsen
//
// This file is part of oAI.
//
// oAI is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// oAI is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
// Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with oAI. If not, see <https://www.gnu.org/licenses/>.


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

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

// MARK: - File Logger

final class FileLogger: @unchecked Sendable {
    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 (read from UserDefaults for thread safety)
    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("oAI.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()
    }

    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)

struct AppLogger {
    let osLogger: Logger
    let category: String

    func debug(_ message: String) {
        FileLogger.shared.write(.debug, category: category, message: message)
        osLogger.debug("\(message, privacy: .public)")
    }

    func info(_ message: String) {
        FileLogger.shared.write(.info, category: category, message: message)
        osLogger.info("\(message, privacy: .public)")
    }

    func warning(_ message: String) {
        FileLogger.shared.write(.warning, category: category, message: message)
        osLogger.warning("\(message, privacy: .public)")
    }

    func error(_ message: String) {
        FileLogger.shared.write(.error, category: category, message: message)
        osLogger.error("\(message, privacy: .public)")
    }
}

// MARK: - Log Namespace

enum Log {
    private static let subsystem = "com.oai.oAI"

    static let api      = AppLogger(osLogger: Logger(subsystem: subsystem, category: "api"), category: "api")
    static let db       = AppLogger(osLogger: Logger(subsystem: subsystem, category: "database"), category: "database")
    static let mcp      = AppLogger(osLogger: Logger(subsystem: subsystem, category: "mcp"), category: "mcp")
    static let settings = AppLogger(osLogger: Logger(subsystem: subsystem, category: "settings"), category: "settings")
    static let search   = AppLogger(osLogger: Logger(subsystem: subsystem, category: "search"), category: "search")
    static let ui       = AppLogger(osLogger: Logger(subsystem: subsystem, category: "ui"), category: "ui")
    static let general  = AppLogger(osLogger: Logger(subsystem: subsystem, category: "general"), category: "general")
}
