import CryptoKit
import Foundation
import XCTest

final class VoiceRecordingParityCheckerTests: XCTestCase {

    func testVoiceRecordingParityBaselineHasNoBlockingFindings() throws {
        let root = URL(fileURLWithPath: #filePath)
            .deletingLastPathComponent()
            .deletingLastPathComponent()

        let baselineURL = root.appendingPathComponent("docs/voice-recording-parity-baseline.json")
        let baselineData = try Data(contentsOf: baselineURL)
        let baselineAny = try JSONSerialization.jsonObject(with: baselineData, options: [])
        guard let baseline = baselineAny as? [String: Any] else {
            XCTFail("invalid baseline format")
            return
        }

        var findings: [[String: String]] = []

        let constants = baseline["constants"] as? [[String: Any]] ?? []
        for spec in constants {
            let id = spec["id"] as? String ?? "unknown"
            let severity = spec["severity"] as? String ?? "P1"
            let file = spec["file"] as? String ?? ""
            let pattern = spec["pattern"] as? String ?? ""
            let expected = spec["expected"] as? String ?? ""
            let rawMatch = spec["raw_match"] as? Bool ?? false

            let rosettaText = try readText(root: root, relativePath: file)
            let actual = regexCapture(text: rosettaText, pattern: pattern, rawMatch: rawMatch)
            if actual == nil {
                findings.append([
                    "severity": severity,
                    "kind": "missing_pattern",
                    "id": id,
                    "evidence": file
                ])
            } else if let actual, actual != expected {
                findings.append([
                    "severity": severity,
                    "kind": "value_mismatch",
                    "id": id,
                    "evidence": file,
                    "actual": actual,
                    "expected": expected
                ])
            }

            if let telegramFile = spec["telegram_file"] as? String,
               let telegramPattern = spec["telegram_pattern"] as? String {
                let telegramText = try readText(root: root, relativePath: telegramFile)
                if regexCapture(text: telegramText, pattern: telegramPattern, rawMatch: true) == nil {
                    findings.append([
                        "severity": severity,
                        "kind": "telegram_reference_missing",
                        "id": id,
                        "evidence": telegramFile
                    ])
                }
            }
        }

        let geometry = baseline["geometry"] as? [[String: Any]] ?? []
        for spec in geometry {
            let id = spec["id"] as? String ?? "unknown"
            let severity = spec["severity"] as? String ?? "P1"
            let file = spec["file"] as? String ?? ""
            let pattern = spec["pattern"] as? String ?? ""
            let expected = spec["expected"] as? String ?? ""
            let rawMatch = spec["raw_match"] as? Bool ?? false

            let rosettaText = try readText(root: root, relativePath: file)
            let actual = regexCapture(text: rosettaText, pattern: pattern, rawMatch: rawMatch)
            if actual == nil {
                findings.append([
                    "severity": severity,
                    "kind": "missing_pattern",
                    "id": id,
                    "evidence": file
                ])
            } else if let actual, actual != expected {
                findings.append([
                    "severity": severity,
                    "kind": "value_mismatch",
                    "id": id,
                    "evidence": file,
                    "actual": actual,
                    "expected": expected
                ])
            }

            if let telegramFile = spec["telegram_file"] as? String,
               let telegramPattern = spec["telegram_pattern"] as? String {
                let telegramText = try readText(root: root, relativePath: telegramFile)
                if regexCapture(text: telegramText, pattern: telegramPattern, rawMatch: true) == nil {
                    findings.append([
                        "severity": severity,
                        "kind": "telegram_reference_missing",
                        "id": id,
                        "evidence": telegramFile
                    ])
                }
            }
        }

        if let flow = baseline["flow"] as? [String: Any] {
            let flowSeverity = flow["severity"] as? String ?? "P1"
            let stateFile = flow["state_file"] as? String ?? ""
            let expectedStates = flow["expected_states"] as? [String] ?? []
            let stateText = try readText(root: root, relativePath: stateFile)
            let actualStates = regexMatches(text: stateText, pattern: "case\\s+([A-Za-z_][A-Za-z0-9_]*)")
            if actualStates != expectedStates {
                findings.append([
                    "severity": flowSeverity,
                    "kind": "state_machine_mismatch",
                    "id": "flow_states",
                    "evidence": stateFile
                ])
            }

            let requiredTransitions = flow["required_transitions"] as? [[String: Any]] ?? []
            for transition in requiredTransitions {
                let transitionId = transition["id"] as? String ?? "unknown"
                let transitionSeverity = transition["severity"] as? String ?? "P1"
                let transitionFile = transition["file"] as? String ?? ""
                let snippet = transition["snippet"] as? String ?? ""
                let transitionText = try readText(root: root, relativePath: transitionFile)
                if !transitionText.contains(snippet) {
                    findings.append([
                        "severity": transitionSeverity,
                        "kind": "transition_missing",
                        "id": transitionId,
                        "evidence": transitionFile
                    ])
                }
            }
        }

        let accessibility = baseline["accessibility"] as? [[String: Any]] ?? []
        for spec in accessibility {
            let id = spec["id"] as? String ?? "unknown"
            let severity = spec["severity"] as? String ?? "P1"
            let file = spec["file"] as? String ?? ""
            let snippet = spec["snippet"] as? String ?? ""
            let text = try readText(root: root, relativePath: file)
            if !text.contains(snippet) {
                findings.append([
                    "severity": severity,
                    "kind": "accessibility_missing",
                    "id": id,
                    "evidence": file
                ])
            }
        }

        let animations = baseline["animations"] as? [[String: Any]] ?? []
        for spec in animations {
            let id = spec["id"] as? String ?? "unknown"
            let severity = spec["severity"] as? String ?? "P1"
            let file = spec["file"] as? String ?? ""
            let snippet = spec["snippet"] as? String ?? ""
            let text = try readText(root: root, relativePath: file)
            if !text.contains(snippet) {
                findings.append([
                    "severity": severity,
                    "kind": "animation_snippet_missing",
                    "id": id,
                    "evidence": file
                ])
            }
        }

        if let assets = baseline["assets"] as? [String: Any] {
            let imagesets = assets["imagesets"] as? [[String: Any]] ?? []
            for imageset in imagesets {
                let assetId = imageset["id"] as? String ?? "unknown"
                let severity = imageset["severity"] as? String ?? "P1"
                let path = imageset["path"] as? String ?? ""
                let files = imageset["files"] as? [[String: Any]] ?? []

                let imagesetURL = root.appendingPathComponent(path)
                if !FileManager.default.fileExists(atPath: imagesetURL.path) {
                    findings.append([
                        "severity": severity,
                        "kind": "asset_missing",
                        "id": assetId,
                        "evidence": path
                    ])
                    continue
                }

                for fileSpec in files {
                    let fileName = fileSpec["name"] as? String ?? ""
                    let expectedSha = fileSpec["sha256"] as? String ?? ""
                    let fileURL = imagesetURL.appendingPathComponent(fileName)
                    if !FileManager.default.fileExists(atPath: fileURL.path) {
                        findings.append([
                            "severity": severity,
                            "kind": "asset_file_missing",
                            "id": "\(assetId)/\(fileName)",
                            "evidence": path
                        ])
                        continue
                    }

                    let actualSha = try sha256(fileURL)
                    if actualSha != expectedSha {
                        findings.append([
                            "severity": severity,
                            "kind": "asset_hash_mismatch",
                            "id": "\(assetId)/\(fileName)",
                            "evidence": path
                        ])
                    }
                }
            }

            let lottie = assets["lottie"] as? [[String: Any]] ?? []
            for lottieSpec in lottie {
                let lottieId = lottieSpec["id"] as? String ?? "unknown"
                let severity = lottieSpec["severity"] as? String ?? "P1"
                let path = lottieSpec["path"] as? String ?? ""
                let expectedSha = lottieSpec["sha256"] as? String ?? ""
                let lottieURL = root.appendingPathComponent(path)

                if !FileManager.default.fileExists(atPath: lottieURL.path) {
                    findings.append([
                        "severity": severity,
                        "kind": "lottie_missing",
                        "id": lottieId,
                        "evidence": path
                    ])
                    continue
                }

                let actualSha = try sha256(lottieURL)
                if actualSha != expectedSha {
                    findings.append([
                        "severity": severity,
                        "kind": "lottie_hash_mismatch",
                        "id": lottieId,
                        "evidence": path
                    ])
                }
            }
        }

        let blocking = findings.filter { finding in
            let severity = finding["severity"] ?? "P3"
            return severity == "P0" || severity == "P1"
        }

        if !blocking.isEmpty {
            let data = try JSONSerialization.data(withJSONObject: blocking, options: [.prettyPrinted, .sortedKeys])
            let details = String(data: data, encoding: .utf8) ?? "<unprintable>"
            XCTFail("blocking voice parity findings:\n\(details)")
        }
    }

    private func readText(root: URL, relativePath: String) throws -> String {
        let url = root.appendingPathComponent(relativePath)
        return try String(contentsOf: url, encoding: .utf8)
    }

    private func sha256(_ url: URL) throws -> String {
        let data = try Data(contentsOf: url)
        let digest = SHA256.hash(data: data)
        return digest.map { String(format: "%02x", $0) }.joined()
    }

    private func regexCapture(text: String, pattern: String, rawMatch: Bool) -> String? {
        guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            return nil
        }
        let nsText = text as NSString
        let range = NSRange(location: 0, length: nsText.length)
        guard let match = regex.firstMatch(in: text, options: [], range: range) else {
            return nil
        }
        if rawMatch {
            return pattern
        }
        guard match.numberOfRanges > 1 else {
            return nil
        }
        let captureRange = match.range(at: 1)
        guard captureRange.location != NSNotFound else {
            return nil
        }
        return nsText.substring(with: captureRange)
    }

    private func regexMatches(text: String, pattern: String) -> [String] {
        guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            return []
        }
        let nsText = text as NSString
        let range = NSRange(location: 0, length: nsText.length)
        return regex.matches(in: text, options: [], range: range).compactMap { match in
            guard match.numberOfRanges > 1 else { return nil }
            let captureRange = match.range(at: 1)
            guard captureRange.location != NSNotFound else { return nil }
            return nsText.substring(with: captureRange)
        }
    }
}
