import Foundation
import OSLog

enum SybilLog {
    private static let subsystem = "com.sybil.ios"

    static let app = Logger(subsystem: subsystem, category: "app")
    static let network = Logger(subsystem: subsystem, category: "network")
    static let ui = Logger(subsystem: subsystem, category: "ui")

    static func info(_ logger: Logger, _ message: String) {
        logger.info("\(message, privacy: .public)")
    }

    static func debug(_ logger: Logger, _ message: String) {
        logger.debug("\(message, privacy: .public)")
    }

    static func warning(_ logger: Logger, _ message: String) {
        logger.warning("\(message, privacy: .public)")
    }

    static func error(_ logger: Logger, _ message: String) {
        logger.error("\(message, privacy: .public)")
    }

    static func error(_ logger: Logger, _ message: String, error: Error) {
        logger.error("\(message, privacy: .public) | \(describe(error), privacy: .public)")
    }

    static func describe(_ error: Error) -> String {
        if let apiError = error as? APIError {
            return apiError.localizedDescription
        }
        if let decodingError = error as? DecodingError {
            return describe(decodingError)
        }
        let nsError = error as NSError
        return "\(nsError.domain) code=\(nsError.code) \(nsError.localizedDescription)"
    }

    static func describe(_ decodingError: DecodingError) -> String {
        switch decodingError {
        case let .typeMismatch(type, context):
            return "Type mismatch for \(type): \(context.debugDescription) at \(codingPath(context.codingPath))"
        case let .valueNotFound(type, context):
            return "Value not found for \(type): \(context.debugDescription) at \(codingPath(context.codingPath))"
        case let .keyNotFound(key, context):
            return "Key \(key.stringValue) not found: \(context.debugDescription) at \(codingPath(context.codingPath))"
        case let .dataCorrupted(context):
            return "Data corrupted: \(context.debugDescription) at \(codingPath(context.codingPath))"
        @unknown default:
            return "Unknown decoding error"
        }
    }

    private static func codingPath(_ path: [CodingKey]) -> String {
        if path.isEmpty {
            return "<root>"
        }
        return path.map(\.stringValue).joined(separator: ".")
    }
}
