import Foundation
import PencilKit
import Vision
import UIKit
import os

private let logger = Logger(subsystem: "com.conjuga.app", category: "HandwritingRecognizer")

/// Result from handwriting recognition including confidence.
struct RecognitionResult: Sendable {
    let text: String
    let confidence: Float  // 0.0 to 1.0
}

/// Recognizes handwritten text from a PKDrawing using Vision framework.
struct HandwritingRecognizer: Sendable {

    /// Minimum confidence to accept a recognition result.
    static let minimumConfidence: Float = 0.3

    /// Recognize text from a PencilKit drawing.
    /// Returns the recognized text and confidence score.
    static func recognize(drawing: PKDrawing) async -> RecognitionResult {
        guard !drawing.strokes.isEmpty else { return RecognitionResult(text: "", confidence: 0) }

        let bounds = drawing.bounds
        guard bounds.width > 0 && bounds.height > 0 else { return RecognitionResult(text: "", confidence: 0) }

        // Render with generous padding and white background for Vision
        let padding: CGFloat = 60
        let renderBounds = bounds.insetBy(dx: -padding, dy: -padding)
        // Enforce minimum size — Vision struggles with tiny images
        let minDimension: CGFloat = 300
        let width = max(renderBounds.width, minDimension)
        let height = max(renderBounds.height, minDimension)
        let size = CGSize(width: width, height: height)

        let scale: CGFloat = 3.0
        let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)

        let renderer = UIGraphicsImageRenderer(size: scaledSize)
        let image = renderer.image { ctx in
            UIColor.white.setFill()
            ctx.fill(CGRect(origin: .zero, size: scaledSize))

            // Center the drawing in the canvas
            let drawingImage = drawing.image(from: renderBounds, scale: scale)
            let xOffset = (scaledSize.width - renderBounds.width * scale) / 2
            let yOffset = (scaledSize.height - renderBounds.height * scale) / 2
            drawingImage.draw(at: CGPoint(x: xOffset, y: yOffset))
        }

        guard let cgImage = image.cgImage else {
            logger.error("Failed to create CGImage from drawing")
            return RecognitionResult(text: "", confidence: 0)
        }

        logger.info("Rendered image: \(Int(scaledSize.width))x\(Int(scaledSize.height)), strokes: \(drawing.strokes.count), drawingBounds: \(Int(bounds.width))x\(Int(bounds.height))")

        return await withCheckedContinuation { continuation in
            let request = VNRecognizeTextRequest { request, error in
                guard error == nil,
                      let observations = request.results as? [VNRecognizedTextObservation] else {
                    continuation.resume(returning: RecognitionResult(text: "", confidence: 0))
                    return
                }

                logger.info("Vision returned \(observations.count) observations")

                var allText: [String] = []
                var totalConfidence: Float = 0
                var count: Float = 0

                for (i, observation) in observations.enumerated() {
                    let candidates = observation.topCandidates(3)
                    for (j, candidate) in candidates.enumerated() {
                        logger.info("  Observation[\(i)] candidate[\(j)]: \"\(candidate.string)\" confidence=\(candidate.confidence)")
                    }
                    if let best = candidates.first {
                        allText.append(best.string)
                        totalConfidence += best.confidence
                        count += 1
                    }
                }

                let text = allText.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
                let avgConfidence = count > 0 ? totalConfidence / count : 0

                logger.info("Final recognized: \"\(text)\" avgConfidence=\(avgConfidence)")
                continuation.resume(returning: RecognitionResult(text: text, confidence: avgConfidence))
            }

            request.recognitionLanguages = ["es", "en"]
            request.recognitionLevel = .accurate
            request.usesLanguageCorrection = true

            let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
            do {
                try handler.perform([request])
            } catch {
                continuation.resume(returning: RecognitionResult(text: "", confidence: 0))
            }
        }
    }

    /// Compare recognized text against expected answer.
    /// Case-insensitive, accent-sensitive comparison with Spanish locale.
    /// Also requires minimum confidence threshold.
    static func checkAnswer(result: RecognitionResult, expected: String) -> Bool {
        let trimmedRecognized = result.text.trimmingCharacters(in: .whitespacesAndNewlines)
        let trimmedExpected = expected.trimmingCharacters(in: .whitespacesAndNewlines)
        let match = !trimmedRecognized.isEmpty
            && result.confidence >= minimumConfidence
            && trimmedRecognized.compare(trimmedExpected, options: .caseInsensitive, locale: Locale(identifier: "es")) == .orderedSame

        logger.info("checkAnswer: recognized=\"\(trimmedRecognized)\" expected=\"\(trimmedExpected)\" confidence=\(result.confidence) minConfidence=\(minimumConfidence) match=\(match)")
        return match
    }
}
