import CoreImage
import UIKit

struct PreparedImage {
    let normalizedImage: UIImage
    let analysisCGImage: CGImage
    let uploadJPEGData: Data
    let thumbnailJPEGData: Data?
}

enum ImagePreprocessorError: LocalizedError {
    case unableToCreateImage

    var errorDescription: String? {
        switch self {
        case .unableToCreateImage:
            return "The selected image could not be prepared for OCR."
        }
    }
}

struct ImagePreprocessor {
    private let ciContext = CIContext()

    func prepare(_ image: UIImage) throws -> PreparedImage {
        let upright = normalized(image)
        let resized = resizedImage(from: upright, maxDimension: 2_048)
        let enhanced = enhancedImage(from: resized) ?? resized

        guard let cgImage = makeCGImage(from: enhanced) else {
            throw ImagePreprocessorError.unableToCreateImage
        }

        return PreparedImage(
            normalizedImage: enhanced,
            analysisCGImage: cgImage,
            uploadJPEGData: enhanced.jpegData(compressionQuality: 0.82) ?? Data(),
            thumbnailJPEGData: resizedImage(from: enhanced, maxDimension: 240).jpegData(compressionQuality: 0.65)
        )
    }

    private func normalized(_ image: UIImage) -> UIImage {
        guard image.imageOrientation != .up else { return image }

        let renderer = UIGraphicsImageRenderer(size: image.size)
        return renderer.image { _ in
            image.draw(in: CGRect(origin: .zero, size: image.size))
        }
    }

    private func resizedImage(from image: UIImage, maxDimension: CGFloat) -> UIImage {
        let largestDimension = max(image.size.width, image.size.height)
        guard largestDimension > maxDimension else { return image }

        let scale = maxDimension / largestDimension
        let targetSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
        let renderer = UIGraphicsImageRenderer(size: targetSize)

        return renderer.image { _ in
            image.draw(in: CGRect(origin: .zero, size: targetSize))
        }
    }

    private func enhancedImage(from image: UIImage) -> UIImage? {
        guard let ciImage = CIImage(image: image) else { return nil }

        let adjusted = ciImage
            .applyingFilter("CIColorControls", parameters: [
                kCIInputContrastKey: 1.08,
                kCIInputSaturationKey: 0.96,
                kCIInputBrightnessKey: 0.01,
            ])
            .applyingFilter("CISharpenLuminance", parameters: [
                kCIInputSharpnessKey: 0.35,
            ])

        guard let cgImage = ciContext.createCGImage(adjusted, from: adjusted.extent) else {
            return nil
        }

        return UIImage(cgImage: cgImage)
    }

    private func makeCGImage(from image: UIImage) -> CGImage? {
        if let cgImage = image.cgImage {
            return cgImage
        }

        if let ciImage = image.ciImage {
            return ciContext.createCGImage(ciImage, from: ciImage.extent)
        }

        let renderer = UIGraphicsImageRenderer(size: image.size)
        let rendered = renderer.image { _ in
            image.draw(in: CGRect(origin: .zero, size: image.size))
        }
        return rendered.cgImage
    }
}
