import Foundation
import LoggingModels
import SearchModels
import SharedConstants

// MARK: - HIGStrategy

extension Search {
    /// Indexes Apple Human Interface Guidelines documentation into the search index.
    ///
    /// HIG files are Markdown documents organised by category (e.g., `foundations`,
    /// `components`, `inputs`).  They apply to all Apple platforms and are indexed
    /// with fixed minimum availability versions that reflect universal HIG coverage.
    ///
    /// URI scheme: `hig://{category}/{filename}`
    ///
    /// ## Example
    /// ```swift
    /// let strategy = Search.HIGStrategy(higDirectory: higDir)
    /// let stats = try await strategy.indexItems(into: index, progress: nil)
    /// ```
    public struct HIGStrategy: SourceIndexingStrategy {
        /// The source identifier written into the FTS index.
        public let source = Shared.Constants.SourcePrefix.hig

        /// Root directory containing the HIG Markdown files.
        public let higDirectory: URL

        /// GoF Strategy seam for log emission (1994 p. 315).
        private let logger: any LoggingModels.Logging.Recording

        /// Create a strategy for indexing Human Interface Guidelines documentation.
        ///
        /// - Parameter higDirectory: The root directory of the HIG corpus.
        public init(
            higDirectory: URL,
            logger: any LoggingModels.Logging.Recording
        ) {
            self.higDirectory = higDirectory
            self.logger = logger
        }

        /// Index all HIG Markdown files found under ``higDirectory``.
        ///
        /// All HIG pages are indexed with universal platform availability
        /// (iOS 2.0 / macOS 10.0 / tvOS 9.0 / watchOS 2.0 / visionOS 1.0).
        ///
        /// - Parameters:
        ///   - index: The ``Search/Index`` to write into.
        ///   - progress: Optional progress callback, called every 100 items.
        /// - Returns: ``Search/IndexStats`` with indexed and skipped counts.
        public func indexItems(
            into index: Search.Index,
            progress: (any Search.IndexingProgressReporting)?
        ) async throws -> Search.IndexStats {
            guard FileManager.default.fileExists(atPath: higDirectory.path) else {
                // #671 — clean-skip when no local corpus is available.
                return IndexStats(
                    source: source,
                    indexed: 0,
                    skipped: 0,
                    wasSkipped: true,
                    skipReason: "no local corpus"
                )
            }

            let markdownFiles = try Search.StrategyHelpers.findMarkdownFiles(in: higDirectory)
            guard !markdownFiles.isEmpty else {
                // #671 — clean-skip when the dir exists but has no HIG pages.
                return IndexStats(
                    source: source,
                    indexed: 0,
                    skipped: 0,
                    wasSkipped: true,
                    skipReason: "no documents found"
                )
            }

            logger.info(
                "🎨 Indexing \(markdownFiles.count) Human Interface Guidelines pages...",
                category: .search
            )

            var indexed = 0
            var skipped = 0

            for (idx, file) in markdownFiles.enumerated() {
                guard let content = try? String(contentsOf: file, encoding: .utf8) else {
                    skipped += 1
                    continue
                }

                let category = Search.StrategyHelpers.extractFrameworkFromPath(
                    file, relativeTo: higDirectory
                ) ?? "general"

                let metadata = Search.StrategyHelpers.extractHIGMetadata(from: content)
                let title = metadata["title"]
                    ?? Search.StrategyHelpers.extractTitle(from: content)
                    ?? file.deletingPathExtension().lastPathComponent

                let filename = file.deletingPathExtension().lastPathComponent
                let uri = "hig://\(category)/\(filename)"

                // #429: indexer-side poison defence — same checks the
                // apple-docs strategy runs, generalised to this source.
                if Search.StrategyHelpers.titleLooksLikeHTTPErrorTemplate(title) {
                    logger.error(
                        "⛔ Skipping HTTP-error-template HIG page (#429 defence): title=\(title.prefix(60)) file=\(file.lastPathComponent)",
                        category: .search
                    )
                    skipped += 1
                    continue
                }
                if Search.StrategyHelpers.contentLooksLikeJavaScriptFallback(content) {
                    logger.error(
                        "⛔ Skipping JS-fallback HIG page (#429 defence): title=\(title.prefix(60)) file=\(file.lastPathComponent)",
                        category: .search
                    )
                    skipped += 1
                    continue
                }

                let contentHash = Shared.Models.HashUtilities.sha256(of: content)
                let attrs = try? FileManager.default.attributesOfItem(atPath: file.path)
                let modDate = attrs?[.modificationDate] as? Date ?? Date()

                do {
                    // #668 — write a structured row in addition to the FTS row so
                    // `docs_structured.(missing)` rate drops from 100 % to 0 % for HIG.
                    // `indexStructuredDocument` writes both rows in one call; the
                    // `.article` kind lets #177 rerank + #616 kind-aware tiebreak
                    // function on these pages instead of being a no-op.
                    let pageURL = URL(string: uri) ?? URL(fileURLWithPath: file.path)
                    let structuredPage = Search.StrategyHelpers.makeArticleStructuredPage(
                        url: pageURL,
                        title: title,
                        rawMarkdown: content,
                        crawledAt: modDate,
                        contentHash: contentHash
                    )
                    let pageJSON = Search.StrategyHelpers.encodeStructuredPageToJSON(structuredPage)

                    // HIG applies universally to all Apple platforms.
                    try await index.indexStructuredDocument(
                        uri: uri,
                        source: source,
                        framework: category,
                        page: structuredPage,
                        jsonData: pageJSON,
                        overrideMinIOS: "2.0",
                        overrideMinMacOS: "10.0",
                        overrideMinTvOS: "9.0",
                        overrideMinWatchOS: "2.0",
                        overrideMinVisionOS: "1.0",
                        overrideAvailabilitySource: "universal"
                    )
                    indexed += 1
                } catch {
                    logger.error(
                        "❌ Failed to index \(uri): \(error)", category: .search
                    )
                    skipped += 1
                }

                if (idx + 1) % Shared.Constants.Interval.progressLogEvery == 0 {
                    progress?.report(processed: idx + 1, total: markdownFiles.count)
                    logger.info(
                        "   Progress: \(idx + 1)/\(markdownFiles.count)", category: .search
                    )
                }
            }

            logger.info(
                "   HIG: \(indexed) indexed, \(skipped) skipped", category: .search
            )
            return IndexStats(source: source, indexed: indexed, skipped: skipped)
        }
    }
}
