//
//  ImagePipeline.swift
//  CheapRetouch
//
//  Preview and export rendering pipeline.
//

import Foundation
import CoreImage
import CoreGraphics
import UIKit

actor ImagePipeline {

    private let context: CIContext
    private let inpaintEngine = InpaintEngine()

    init() {
        self.context = CIContext(options: [
            .useSoftwareRenderer: false,
            .highQualityDownsample: true
        ])
    }

    func renderPreview(
        originalImage: CGImage,
        operations: [EditOperation],
        maxSize: Int = 2048
    ) async throws -> CGImage {
        // Scale down for preview if needed
        var image = originalImage

        let scaleFactor: CGFloat
        if image.width > maxSize || image.height > maxSize {
            scaleFactor = CGFloat(maxSize) / CGFloat(max(image.width, image.height))
            image = try scaleImage(image, scale: scaleFactor)
        } else {
            scaleFactor = 1.0
        }

        // Apply operations
        image = try await applyOperations(operations, to: image, scaleFactor: scaleFactor)

        return image
    }

    func renderExport(
        originalImage: CGImage,
        operations: [EditOperation]
    ) async throws -> CGImage {
        // Apply operations at full resolution
        var image = originalImage
        image = try await applyOperations(operations, to: image, scaleFactor: 1.0)
        return image
    }

    private func applyOperations(
        _ operations: [EditOperation],
        to image: CGImage,
        scaleFactor: CGFloat
    ) async throws -> CGImage {
        var currentImage = image

        // Group mask and inpaint operations
        var pendingMask: MaskOperation?

        for operation in operations {
            switch operation {
            case .mask(let maskOp):
                pendingMask = maskOp

            case .inpaint:
                if let maskOp = pendingMask {
                    // Create mask data and convert to CGImage on MainActor for Swift 6 compatibility
                    let mask = await MainActor.run {
                        let maskData = MaskData(
                            width: maskOp.maskWidth,
                            height: maskOp.maskHeight,
                            data: maskOp.maskData
                        )
                        return maskData.toCGImage()
                    }
                    
                    if let mask = mask {
                        // Scale mask if needed
                        let scaledMask: CGImage
                        if scaleFactor != 1.0 {
                            scaledMask = try scaleImage(mask, scale: scaleFactor)
                        } else {
                            scaledMask = mask
                        }

                        // Apply inpainting
                        currentImage = try await inpaintEngine.inpaint(
                            image: currentImage,
                            mask: scaledMask
                        )
                    }
                }
                pendingMask = nil

            case .adjustment(let adjustment):
                currentImage = applyAdjustment(adjustment, to: currentImage) ?? currentImage
            }
        }

        return currentImage
    }

    private func applyAdjustment(_ adjustment: AdjustmentOperation, to image: CGImage) -> CGImage? {
        switch adjustment.type {
        case .brightness:
            return applyColorAdjustment(to: image, brightness: adjustment.value)
        case .contrast:
            return applyColorAdjustment(to: image, contrast: adjustment.value)
        case .saturation:
            return applyColorAdjustment(to: image, saturation: adjustment.value)
        }
    }

    func applyColorAdjustment(
        to image: CGImage,
        brightness: Float = 0,
        contrast: Float = 1,
        saturation: Float = 1
    ) -> CGImage? {
        let ciImage = CIImage(cgImage: image)

        guard let filter = CIFilter(name: "CIColorControls") else {
            return nil
        }

        filter.setValue(ciImage, forKey: kCIInputImageKey)
        filter.setValue(brightness, forKey: kCIInputBrightnessKey)
        filter.setValue(contrast, forKey: kCIInputContrastKey)
        filter.setValue(saturation, forKey: kCIInputSaturationKey)

        guard let outputImage = filter.outputImage else {
            return nil
        }

        return context.createCGImage(outputImage, from: outputImage.extent)
    }

    private func scaleImage(_ image: CGImage, scale: CGFloat) throws -> CGImage {
        let newWidth = Int(CGFloat(image.width) * scale)
        let newHeight = Int(CGFloat(image.height) * scale)

        guard let context = CGContext(
            data: nil,
            width: newWidth,
            height: newHeight,
            bitsPerComponent: 8,
            bytesPerRow: newWidth * 4,
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
        ) else {
            throw ImagePipelineError.contextCreationFailed
        }

        context.interpolationQuality = .high
        context.draw(image, in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))

        guard let result = context.makeImage() else {
            throw ImagePipelineError.renderFailed
        }

        return result
    }

    enum ImagePipelineError: Error {
        case contextCreationFailed
        case renderFailed
    }
}
