import ExpoModulesCore
import UIKit

public class ImageEditorViewManager: Module {
    public func definition() -> ModuleDefinition {
        Name("ImageEditorView")

        // MARK: - View Definition

        View(ImageEditorViewComponent.self) {
            // MARK: - Events

            Events(
                "onReady",
                "onImageLoaded",
                "onImageChanged",
                "onError"
            )

            // MARK: - Props

            Prop("source") { (view: ImageEditorViewComponent, source: [String: Any]?) in
                guard let source = source else { return }

                if let uri = source["uri"] as? String {
                    view.loadImage(from: uri)
                } else if let path = source["path"] as? String {
                    view.loadImage(from: path)
                }
            }

            Prop("adjustments") { (view: ImageEditorViewComponent, adjustments: [String: Any]?) in
                guard let adjustments = adjustments else { return }
                view.applyAdjustments(adjustments)
            }

            Prop("filter") { (view: ImageEditorViewComponent, filter: [String: Any]?) in
                guard let filter = filter else { return }

                if let type = filter["type"] as? String {
                    let intensity = filter["intensity"] as? Float ?? 1.0
                    view.applyFilter(type, intensity: intensity)
                }
            }

            Prop("drawingEnabled") { (view: ImageEditorViewComponent, enabled: Bool) in
                view.setDrawingEnabled(enabled)
            }

            Prop("drawingBrushSize") { (view: ImageEditorViewComponent, size: Double) in
                view.setDrawingBrushSize(CGFloat(size))
            }

            Prop("drawingColor") { (view: ImageEditorViewComponent, color: String) in
                view.setDrawingColor(color)
            }

            // MARK: - Methods (called from JS via ref)

            AsyncFunction("crop") { (view: ImageEditorViewComponent, x: Double, y: Double, width: Double, height: Double) in
                let rect = CGRect(x: x, y: y, width: width, height: height)
                view.crop(to: rect)
            }
            .runOnQueue(.main)

            AsyncFunction("rotate") { (view: ImageEditorViewComponent, angle: Double) in
                view.rotate(by: CGFloat(angle))
            }
            .runOnQueue(.main)

            AsyncFunction("flipHorizontal") { (view: ImageEditorViewComponent) in
                view.flipHorizontal()
            }
            .runOnQueue(.main)

            AsyncFunction("flipVertical") { (view: ImageEditorViewComponent) in
                view.flipVertical()
            }
            .runOnQueue(.main)

            AsyncFunction("undo") { (view: ImageEditorViewComponent) in
                view.undo()
            }
            .runOnQueue(.main)

            AsyncFunction("redo") { (view: ImageEditorViewComponent) in
                view.redo()
            }
            .runOnQueue(.main)

            AsyncFunction("export") { (view: ImageEditorViewComponent, options: [String: Any]) -> [String: Any] in
                return try view.exportImage(options: options)
            }
            .runOnQueue(.main)
        }
    }
}

// MARK: - Native View Component

public class ImageEditorViewComponent: ExpoView {
    private let editorView: ImageEditorView

    // Event emitters
    var onReady: EventDispatcher?
    var onImageLoaded: EventDispatcher?
    var onImageChanged: EventDispatcher?
    var onError: EventDispatcher?

    public override required init(appContext: AppContext? = nil) {
        self.editorView = ImageEditorView(frame: .zero)
        super.init(appContext: appContext)

        setupView()
    }

    private func setupView() {
        editorView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        editorView.delegate = self
        addSubview(editorView)

        // Notify that view is ready
        DispatchQueue.main.async {
            self.onReady?([:])
        }
    }

    public override func layoutSubviews() {
        super.layoutSubviews()
        editorView.frame = bounds
    }

    // MARK: - Public Methods

    func loadImage(from path: String) {
        guard let url = URL(string: path) else {
            onError?(["error": "Invalid image path"])
            return
        }

        editorView.loadImage(from: url)
    }

    func applyAdjustments(_ params: [String: Any]) {
        var adjustments = ImageAdjustments()
        adjustments.brightness = (params["brightness"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.contrast = (params["contrast"] as? NSNumber)?.floatValue ?? 1.0
        adjustments.saturation = (params["saturation"] as? NSNumber)?.floatValue ?? 1.0
        adjustments.exposure = (params["exposure"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.highlights = (params["highlights"] as? NSNumber)?.floatValue ?? 1.0
        adjustments.shadows = (params["shadows"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.temperature = (params["temperature"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.tint = (params["tint"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.sharpness = (params["sharpness"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.noiseReduction = (params["noiseReduction"] as? NSNumber)?.floatValue ?? 0.0
        adjustments.vignette = (params["vignette"] as? NSNumber)?.floatValue ?? 0.0

        editorView.applyAdjustments(adjustments)
    }

    func applyFilter(_ filterType: String, intensity: Float) {
        guard let preset = FilterPreset(rawValue: filterType) else {
            onError?(["error": "Invalid filter type"])
            return
        }

        editorView.applyFilter(preset, intensity: intensity)
    }

    func crop(to rect: CGRect) {
        editorView.crop(to: rect)
    }

    func rotate(by angle: CGFloat) {
        editorView.rotate(by: angle)
    }

    func flipHorizontal() {
        editorView.flipHorizontal()
    }

    func flipVertical() {
        editorView.flipVertical()
    }

    func undo() {
        editorView.undo()
    }

    func redo() {
        editorView.redo()
    }

    func setDrawingEnabled(_ enabled: Bool) {
        if enabled {
            editorView.startDrawing()
        } else {
            editorView.stopDrawing()
        }
    }

    func setDrawingBrushSize(_ size: CGFloat) {
        editorView.drawingBrushSize = size
    }

    func setDrawingColor(_ colorString: String) {
        editorView.drawingColor = UIColor.from(hex: colorString) ?? .black
    }

    func exportImage(options: [String: Any]) throws -> [String: Any] {
        let formatString = options["format"] as? String ?? "jpeg"
        let quality = options["quality"] as? Float ?? 0.9

        let format: ExportFormat
        switch formatString.lowercased() {
        case "png":
            format = .png
        case "heic":
            format = .heic
        default:
            format = .jpeg
        }

        let data = try editorView.exportImage(format: format, quality: quality)

        // Save to temporary file
        let tempDir = FileManager.default.temporaryDirectory
        let filename = "exported_\(UUID().uuidString).\(formatString)"
        let fileURL = tempDir.appendingPathComponent(filename)

        try data.write(to: fileURL)

        return [
            "success": true,
            "uri": fileURL.absoluteString,
            "size": data.count
        ]
    }
}

// MARK: - ImageEditorViewDelegate

extension ImageEditorViewComponent: ImageEditorViewDelegate {
    public func imageEditorView(_ view: ImageEditorView, didLoadImage image: UIImage) {
        onImageLoaded?([
            "width": image.size.width,
            "height": image.size.height
        ])
    }

    public func imageEditorView(_ view: ImageEditorView, didUpdateImage image: UIImage) {
        onImageChanged?([
            "width": image.size.width,
            "height": image.size.height
        ])
    }

    public func imageEditorView(_ view: ImageEditorView, didFinishDrawing path: DrawingPath) {
        onImageChanged?([:])
    }

    public func imageEditorView(_ view: ImageEditorView, didFailWithError error: Error) {
        onError?(["error": error.localizedDescription])
    }
}

// MARK: - UIColor Extension

extension UIColor {
    static func from(hex: String) -> UIColor? {
        var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
        hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")

        var rgb: UInt64 = 0

        guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }

        let length = hexSanitized.count
        let r, g, b, a: CGFloat

        if length == 6 {
            r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
            g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
            b = CGFloat(rgb & 0x0000FF) / 255.0
            a = 1.0
        } else if length == 8 {
            r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
            g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
            b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
            a = CGFloat(rgb & 0x000000FF) / 255.0
        } else {
            return nil
        }

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }
}

