import AppKit
import Foundation

@MainActor
public protocol ApplicationIconResolving {
    func applicationURL(forBundleIdentifier bundleIdentifier: String) -> URL?
    func icon(forFile path: String) -> NSImage
    func fallbackApplicationIcon() -> NSImage
}

@MainActor
public final class WorkspaceApplicationIconResolver: ApplicationIconResolving {
    public init() {}

    public func applicationURL(forBundleIdentifier bundleIdentifier: String) -> URL? {
        NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleIdentifier)
    }

    public func icon(forFile path: String) -> NSImage {
        NSWorkspace.shared.icon(forFile: path)
    }

    public func fallbackApplicationIcon() -> NSImage {
        NSWorkspace.shared.icon(for: .applicationBundle)
    }
}

@MainActor
public final class WorkspaceAppIconProvider: AppIconProviderProtocol {
    private let resolver: any ApplicationIconResolving
    private var iconCache: [String: NSImage] = [:]

    public init(resolver: any ApplicationIconResolving = WorkspaceApplicationIconResolver()) {
        self.resolver = resolver
    }

    public func icon(for app: AlignerApp) -> NSImage {
        let key = cacheKey(for: app)
        if let cachedIcon = iconCache[key] {
            return cachedIcon
        }

        let icon = resolvedIcon(for: app)
        iconCache[key] = icon
        return icon
    }

    public func fallbackIcon() -> NSImage {
        resolver.fallbackApplicationIcon()
    }

    private func resolvedIcon(for app: AlignerApp) -> NSImage {
        if let applicationURL = resolver.applicationURL(forBundleIdentifier: app.bundleIdentifier) {
            let icon = resolver.icon(forFile: applicationURL.path)
            if icon.isValid {
                return icon
            }
        }

        if let executablePath = app.executablePath {
            let icon = resolver.icon(forFile: executablePath)
            if icon.isValid {
                return icon
            }
        }

        return fallbackIcon()
    }

    private func cacheKey(for app: AlignerApp) -> String {
        [
            app.bundleIdentifier,
            app.executablePath ?? "",
            app.processIdentifier.map(String.init) ?? ""
        ].joined(separator: "|")
    }
}
