import Core
import CorePackageIndexing
import CoreProtocols
import Foundation
import Resources
import SharedConstants
/// Open a package's GitHub page in the default browser
@MainActor
func openCurrentPackageInBrowser(state: AppState) {
    let visible = state.visiblePackages
    guard state.cursor < visible.count else { return }

    let package = visible[state.cursor].package
    let url = package.url

    // Use macOS 'open' command to open URL in default browser
    let process = Process()
    process.executableURL = URL(fileURLWithPath: "/usr/bin/open")
    process.arguments = [url]

    do {
        try process.run()
    } catch {
        // Silently fail - don't crash the TUI
    }
}

/// User-writable location for selected packages: ~/.cupertino/selected-packages.json
private var userPackageSelectionsURL: URL {
    Shared.Paths.live().baseDirectory
        .appendingPathComponent(Shared.Constants.FileName.selectedPackages)
}

/// Load selected package URLs from user file (~/.cupertino/selected-packages.json)
/// Returns empty set if file doesn't exist (will use bundled priority-packages.json defaults)
func loadUserSelectedPackageURLs() -> Set<String> {
    let fileURL = userPackageSelectionsURL

    guard FileManager.default.fileExists(atPath: fileURL.path) else {
        return []
    }

    do {
        let data = try Data(contentsOf: fileURL)
        guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
              let tiers = json["tiers"] as? [String: Any] else {
            return []
        }

        var urls = Set<String>()
        for (_, tierValue) in tiers {
            if let tier = tierValue as? [String: Any],
               let packages = tier["packages"] as? [[String: Any]] {
                for pkg in packages {
                    if let url = pkg["url"] as? String {
                        urls.insert(url)
                    }
                }
            }
        }
        return urls
    } catch {
        return []
    }
}

/// Save selected packages to ~/.cupertino/selected-packages.json
@MainActor
func saveSelections(state: AppState) throws {
    let selected = state.packages.filter(\.isSelected).map(\.package)

    // Convert to priority package format
    let priorityPackages = selected.map { pkg in
        Core.PackageIndexing.PriorityPackage(owner: pkg.owner, repo: pkg.repo, url: pkg.url)
    }

    // Create catalog JSON structure matching PriorityPackagesCatalogJSON format
    let catalogJSON: [String: Any] = [
        "version": "1.0",
        "lastUpdated": ISO8601DateFormatter().string(from: Date()),
        "description": "Curated list of high-priority Swift packages (TUI generated)",
        "tiers": [
            "apple_official": [
                "description": "Apple official packages",
                "owner": "apple",
                "count": 0,
                "packages": [] as [[String: Any]],
            ],
            "ecosystem": [
                "description": "Essential ecosystem packages",
                "count": priorityPackages.count,
                "packages": priorityPackages.map { [
                    "owner": $0.owner ?? "",
                    "repo": $0.repo,
                    "url": $0.url,
                ] },
            ],
        ],
        "stats": [
            "totalCriticalApplePackages": 0,
            "totalEcosystemPackages": priorityPackages.count,
            "totalPriorityPackages": priorityPackages.count,
        ],
    ]

    // Ensure ~/.cupertino directory exists
    let baseDir = Shared.Paths.live().baseDirectory
    if !FileManager.default.fileExists(atPath: baseDir.path) {
        try FileManager.default.createDirectory(at: baseDir, withIntermediateDirectories: true)
    }

    // Write to user-writable location
    let data = try JSONSerialization.data(withJSONObject: catalogJSON, options: [.prettyPrinted, .sortedKeys])
    try data.write(to: userPackageSelectionsURL)

    // Also persist the exclusion list so `x` toggles survive across sessions.
    try saveExclusions(state: state)

    state.statusMessage = "✅ Saved \(selected.count) selected, \(state.packages.filter(\.isExcluded).count) excluded"
}

/// Location of the user exclusion list.
private var userExclusionsURL: URL {
    Shared.Paths.live().baseDirectory
        .appendingPathComponent(Shared.Constants.FileName.excludedPackages)
}

/// Load excluded "owner/repo" entries from disk; empty set if absent/malformed.
/// TUI is a composition root binary, so resolving the base directory via
/// `Shared.Paths.live()` here is the right shape (per the path-DI arc, #535).
@MainActor
func loadExcludedPackages() -> Set<String> {
    Core.PackageIndexing.ExclusionList.load(from: Shared.Paths.live().baseDirectory)
}

/// Load the resolved closure's non-seed entries so the TUI can flag them as
/// "discovered via dependency walking" in the package list. Missing file → empty set.
@MainActor
func loadDiscoveredPackages() -> Set<String> {
    let fileURL = Shared.Paths.live().baseDirectory
        .appendingPathComponent(Shared.Constants.FileName.resolvedPackages)
    guard let store = Core.PackageIndexing.ResolvedPackagesStore.load(from: fileURL) else {
        return []
    }
    var out = Set<String>()
    for pkg in store.packages {
        // Seeds list themselves as their sole parent; discovered packages list a
        // different seed or multiple parents. If there's any parent that isn't the
        // package itself, treat it as discovered.
        let selfKey = "\(pkg.owner.lowercased())/\(pkg.repo.lowercased())"
        if pkg.parents.contains(where: { $0.lowercased() != selfKey }) {
            out.insert(selfKey)
        }
    }
    return out
}

/// Persist the exclusion list as a flat JSON array of "owner/repo" strings.
@MainActor
func saveExclusions(state: AppState) throws {
    let excluded = state.packages.filter(\.isExcluded).map {
        "\($0.package.owner)/\($0.package.repo)"
    }.sorted()

    let baseDir = Shared.Paths.live().baseDirectory
    if !FileManager.default.fileExists(atPath: baseDir.path) {
        try FileManager.default.createDirectory(at: baseDir, withIntermediateDirectories: true)
    }
    let encoder = JSONEncoder()
    encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
    let data = try encoder.encode(excluded)
    try data.write(to: userExclusionsURL)
}

/// Open a URL (file or directory) in Finder
func openInFinder(url: URL) {
    let process = Process()
    process.executableURL = URL(fileURLWithPath: "/usr/bin/open")
    process.arguments = [url.path]

    do {
        try process.run()
    } catch {
        // Silently fail
    }
}
