import Foundation
import Resources
import SharedConstants

/// Catalog of archive guides loaded from bundled JSON resource
enum ArchiveGuidesCatalog {
    /// User-writable location for selected guides: `<baseDirectory>/selected-archive-guides.json`.
    ///
    /// Delegates to `Shared.Paths.userArchiveSelectionsFile` (#101) — the
    /// crawler's `Crawler.ArchiveGuideCatalog.userSelectionsFileURL(baseDirectory:)`
    /// reads from the same seam, so the TUI's write target and the crawler's
    /// read target cannot drift. Pre-#101 each side computed the literal
    /// filename independently; a rename on either side would silently split
    /// what the TUI wrote from what the crawler read.
    ///
    /// Access level is `internal` (not `private`) only so `@testable import TUI`
    /// in `TUITests` can assert the delegation. Production callers stay
    /// inside this file.
    static var userSelectionsURL: URL {
        Shared.Paths.live().userArchiveSelectionsFile
    }

    /// All archive guides from the embedded catalog (#161).
    static var allGuides: [ArchiveEntry] {
        guard let data = Resources.jsonData(named: "archive-guides-catalog") else {
            return []
        }

        do {
            let catalog = try JSONDecoder().decode(ArchiveGuidesCatalogJSON.self, from: data)
            return catalog.guides.map { guide in
                ArchiveEntry(
                    title: guide.title,
                    framework: guide.framework,
                    category: guide.category,
                    path: guide.path,
                    description: guide.description,
                    isSelected: guide.required, // Required entries start selected
                    isDownloaded: false,
                    isRequired: guide.required
                )
            }
        } catch {
            return []
        }
    }

    /// Load selected guides from user-writable location (~/.cupertino/selected-archive-guides.json)
    /// If file doesn't exist, creates it from bundled catalog with required guides selected
    static func loadSelectedGuidePaths() -> Set<String> {
        let selectedURL = userSelectionsURL

        // If user file doesn't exist, create it from bundled catalog
        if !FileManager.default.fileExists(atPath: selectedURL.path) {
            createDefaultSelectionsFile()
        }

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

        do {
            let data = try Data(contentsOf: selectedURL)
            let selected = try JSONDecoder().decode(SelectedArchiveGuidesJSON.self, from: data)
            return Set(selected.guides.map(\.path))
        } catch {
            return []
        }
    }

    /// Create default selections file from embedded catalog (required guides only, #161).
    private static func createDefaultSelectionsFile() {
        guard let data = Resources.jsonData(named: "archive-guides-catalog") else {
            return
        }

        do {
            // 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)
            }

            let catalog = try JSONDecoder().decode(ArchiveGuidesCatalogJSON.self, from: data)
            let requiredGuides = catalog.guides.filter(\.required)

            // Create selections JSON with required guides
            let json = SelectedArchiveGuidesJSON(
                version: "1.0",
                lastUpdated: ISO8601DateFormatter().string(from: Date()),
                description: "Selected Apple Archive guides for crawling (auto-generated with required guides)",
                count: requiredGuides.count,
                guides: requiredGuides.map { guide in
                    SelectedGuideJSON(
                        title: guide.title,
                        framework: guide.framework,
                        category: guide.category,
                        path: guide.path
                    )
                }
            )

            let encoder = JSONEncoder()
            encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
            let outputData = try encoder.encode(json)
            try outputData.write(to: userSelectionsURL)
        } catch {
            // Silently fail - will fall back to bundled defaults
        }
    }

    /// Save selected guides to user-writable location
    static func saveSelectedGuides(_ guides: [ArchiveEntry]) throws {
        // 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)
        }

        let selected = guides.filter(\.isSelected)
        let json = SelectedArchiveGuidesJSON(
            version: "1.0",
            lastUpdated: ISO8601DateFormatter().string(from: Date()),
            description: "Selected Apple Archive guides for crawling (TUI generated)",
            count: selected.count,
            guides: selected.map { guide in
                SelectedGuideJSON(
                    title: guide.title,
                    framework: guide.framework,
                    category: guide.category,
                    path: guide.path
                )
            }
        )

        let encoder = JSONEncoder()
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
        let data = try encoder.encode(json)
        try data.write(to: userSelectionsURL)
    }
}

// MARK: - JSON Codable Types (file-private to this catalog)

private struct ArchiveGuidesCatalogJSON: Codable {
    let count: Int
    let guides: [ArchiveGuideJSON]
    let baseURL: String
}

private struct ArchiveGuideJSON: Codable {
    let title: String
    let framework: String
    let category: String
    let path: String
    let description: String
    let required: Bool
}

private struct SelectedArchiveGuidesJSON: Codable {
    let version: String
    let lastUpdated: String
    let description: String
    let count: Int
    let guides: [SelectedGuideJSON]
}

private struct SelectedGuideJSON: Codable {
    let title: String
    let framework: String
    let category: String
    let path: String
}
