import Foundation
import ExpoModulesCore
import PencilKit
import UIKit

public class ExpoPencilKitModule: Module {
    // Single canvas view approach like the working example
    private var canvasView: PKCanvasView?
    private var toolPicker: PKToolPicker?
    private var undoManager: UndoManager?
    private var colorPickerViewController: UIColorPickerViewController?
    private var colorPickerDelegate: ColorPickerDelegate?
    private var toolPickerObserver: ToolPickerObserver?

    // Store current tool state for custom tool picker
    private var currentToolType: String = "pen"
    private var currentColor: UIColor = UIColor.black
    private var currentWidth: CGFloat = 10.0
    private var currentEraserType: String = "bitmap"

    public func definition() -> ModuleDefinition {
        Name("ExpoPencilKitModule")

        // Module lifecycle
        OnCreate {
            // Register this module instance with the view
            ExpoPencilKitView.setModuleInstance(self)
        }

        // View manager for PencilKit canvas
        View(ExpoPencilKitView.self) {
            Events("onDrawStart", "onDrawEnd", "onDrawChange", "onCanUndoChanged", "onCanRedoChanged", "onZoomChanged")

            Prop("imagePath") { (view: ExpoPencilKitView, imagePath: [String: Any]?) in
                view.setImagePath(imagePath)
            }
            Prop("drawingPolicy") { (view: ExpoPencilKitView, policy: String) in
                view.setDrawingPolicy(policy)
            }
        }

        // Setup tool picker for a specific canvas
        AsyncFunction("setupToolPicker") { (viewTag: Int) in
            await MainActor.run {
                self.setupToolPicker(for: viewTag)
            }
        }

        // Clear drawing from canvas
        AsyncFunction("clearDrawing") { (viewTag: Int) in
            await MainActor.run {
                self.clearDrawing()
            }
        }

        // Undo last drawing action
        AsyncFunction("undo") { (viewTag: Int) in
            await MainActor.run {
                self.undoDrawing()
            }
        }

        // Redo last undone drawing action
        AsyncFunction("redo") { (viewTag: Int) in
            await MainActor.run {
                self.redoDrawing()
            }
        }

        // Capture drawing as PNG image
        AsyncFunction("captureDrawing") { (viewTag: Int) -> String in
            return await MainActor.run {
                return self.captureDrawing()
            }
        }

        // Get canvas data as base64
        AsyncFunction("getCanvasDataAsBase64") { (viewTag: Int) -> String in
            return await MainActor.run {
                return self.getCanvasDataAsBase64()
            }
        }

        // Set canvas data from base64
        AsyncFunction("setCanvasDataFromBase64") { (viewTag: Int, base64String: String) -> Bool in
            return await MainActor.run {
                return self.setCanvasDataFromBase64(base64String: base64String)
            }
        }

        // Check if undo is possible
        AsyncFunction("canUndo") { (viewTag: Int) -> Bool in
            return await MainActor.run {
                return self.canPerformUndo()
            }
        }

        // Check if redo is possible
        AsyncFunction("canRedo") { (viewTag: Int) -> Bool in
            return await MainActor.run {
                return self.canPerformRedo()
            }
        }

        // Show color picker
        AsyncFunction("showColorPicker") { (viewTag: Int) in
            await MainActor.run {
                self.showColorPicker()
            }
        }

        // Set canvas background color
        AsyncFunction("setCanvasBackgroundColor") { (viewTag: Int, colorString: String) in
            await MainActor.run {
                self.setCanvasBackgroundColor(colorString)
            }
        }

        // Get canvas background color
        AsyncFunction("getCanvasBackgroundColor") { (viewTag: Int) -> String in
            return await MainActor.run {
                return self.getCanvasBackgroundColor()
            }
        }

        AsyncFunction("setZoomScale") { (viewTag: Int, zoomScale: Double) in
            await MainActor.run {
                if let view = self.findView(byTag: viewTag) as? ExpoPencilKitView {
                    view.setZoomScale(zoomScale)
                }
            }
        }

        AsyncFunction("setContentOffset") { (viewTag: Int, contentOffset: [String: Double], animated: Bool) in
            await MainActor.run {
                if let view = self.findView(byTag: viewTag) as? ExpoPencilKitView {
                    view.setContentOffset(contentOffset, animated: animated)
                }
            }
        }

        // MARK: - New Drawing Tool Methods

        // Set drawing tool with type, color, and width
        AsyncFunction("setDrawingTool") { (viewTag: Int, toolType: String, colorString: String, width: Double, eraserType: String?) in
            await MainActor.run {
                self.setDrawingTool(toolType: toolType, color: colorString, width: CGFloat(width), eraserType: eraserType)
            }
        }

        // Get current drawing tool properties
        AsyncFunction("getCurrentDrawingTool") { (viewTag: Int) -> [String: Any] in
            return await MainActor.run {
                return self.getCurrentDrawingTool()
            }
        }

        // Set drawing color only
        AsyncFunction("setDrawingColor") { (viewTag: Int, colorString: String) in
            await MainActor.run {
                self.setDrawingColor(colorString)
            }
        }

        // Set drawing width only
        AsyncFunction("setDrawingWidth") { (viewTag: Int, width: Double) in
            await MainActor.run {
                self.setDrawingWidth(CGFloat(width))
            }
        }

        // Set drawing tool type only
        AsyncFunction("setDrawingToolType") { (viewTag: Int, toolType: String) in
            await MainActor.run {
                self.setDrawingToolType(toolType)
            }
        }

        // Set eraser tool with specific type
        AsyncFunction("setEraserTool") { (viewTag: Int, eraserType: String) in
            await MainActor.run {
                self.setEraserTool(eraserType: eraserType)
            }
        }

        // Set lasso selection tool
        AsyncFunction("setLassoTool") { (viewTag: Int) in
            await MainActor.run {
                self.setLassoTool()
            }
        }
    }

    private func findView(byTag tag: Int) -> UIView? {
        return UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first(where: { $0.isKeyWindow })?
            .rootViewController?
            .view
            .viewWithTag(tag)
    }

    // MARK: - Canvas Registration

    func registerCanvasView(_ canvas: PKCanvasView) {
        self.canvasView = canvas
        // Set initial tool
        updateCanvasTool()
    }

    func unregisterCanvasView() {
        self.canvasView = nil
        self.toolPicker = nil
        self.undoManager = nil
    }

    // MARK: - Private Methods

    private func setupToolPicker(for viewTag: Int) {
        guard let canvasView = self.canvasView else {
            return
        }

        toolPicker = PKToolPicker()

        // Configure tool picker
        toolPicker?.setVisible(true, forFirstResponder: canvasView)
        toolPicker?.addObserver(canvasView)

        // Create and add tool picker observer
        toolPickerObserver = ToolPickerObserver()
        toolPicker?.addObserver(toolPickerObserver!)

        // Make canvas view first responder
        canvasView.becomeFirstResponder()

        // Get the undo manager from canvas view
        undoManager = canvasView.undoManager
    }

    private func clearDrawing() {
        canvasView?.drawing = PKDrawing()
    }

    private func undoDrawing() {
        guard let undoManager = undoManager else {
            return
        }

        if undoManager.canUndo {
            undoManager.undo()
        }
    }

    private func redoDrawing() {
        guard let undoManager = undoManager else {
            return
        }

        if undoManager.canRedo {
            undoManager.redo()
        }
    }

    private func captureDrawing() -> String {
        guard let canvasView = canvasView else {
            return ""
        }

        let renderer = UIGraphicsImageRenderer(bounds: canvasView.bounds)
        let image = renderer.image { context in
            canvasView.drawHierarchy(in: canvasView.bounds, afterScreenUpdates: false)
        }

        guard let imageData = image.pngData() else {
            return ""
        }

        let base64String = imageData.base64EncodedString()
        return base64String
    }

    private func getCanvasDataAsBase64() -> String {
        guard let canvasView = canvasView else {
            return ""
        }

        let drawingData = canvasView.drawing.dataRepresentation()
        let base64String = drawingData.base64EncodedString()
        return base64String
    }

    private func setCanvasDataFromBase64(base64String: String) -> Bool {
        guard let canvasView = canvasView else {
            return false
        }

        guard let drawingData = Data(base64Encoded: base64String) else {
            return false
        }

        do {
            let drawing = try PKDrawing(data: drawingData)
            canvasView.drawing = drawing
            return true
        } catch {
            return false
        }
    }

    private func canPerformUndo() -> Bool {
        guard let undoManager = undoManager else {
            return false
        }

        return undoManager.canUndo
    }

    private func canPerformRedo() -> Bool {
        guard let undoManager = undoManager else {
            return false
        }

        return undoManager.canRedo
    }

    private func showColorPicker() {
        // Get the key window scene and root view controller
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
              let rootViewController = windowScene.windows.first?.rootViewController else {
            return
        }

        // Find the topmost presented view controller
        var topViewController = rootViewController
        while let presentedViewController = topViewController.presentedViewController {
            topViewController = presentedViewController
        }

        colorPickerViewController = UIColorPickerViewController()
        colorPickerDelegate = ColorPickerDelegate(module: self)
        colorPickerViewController?.delegate = colorPickerDelegate

        topViewController.present(colorPickerViewController!, animated: true)
    }

    private func setCanvasBackgroundColor(_ colorString: String) {
        let color = colorFromHexString(colorString)
        canvasView?.backgroundColor = color
    }

    private func getCanvasBackgroundColor() -> String {
        guard let canvasView = canvasView else {
            return "FFFFFF" // Default to white
        }

        return hexStringFromColor(canvasView.backgroundColor ?? UIColor.white)
    }

    // MARK: - Drawing Tool Methods

    private func setDrawingTool(toolType: String, color: String, width: CGFloat, eraserType: String?) {
        currentToolType = toolType
        currentColor = colorFromHexString(color)
        currentWidth = width
        if let eraserType = eraserType {
            currentEraserType = eraserType
        }
        updateCanvasTool()
    }

    private func getCurrentDrawingTool() -> [String: Any] {
        var result: [String: Any] = [
            "toolType": currentToolType,
            "color": hexStringFromColor(currentColor),
            "width": currentWidth
        ]

        if currentToolType == "eraser" {
            result["eraserType"] = currentEraserType
        }

        return result
    }

    private func setDrawingColor(_ colorString: String) {
        currentColor = colorFromHexString(colorString)
        updateCanvasTool()
    }

    private func setDrawingWidth(_ width: CGFloat) {
        currentWidth = width
        updateCanvasTool()
    }

    private func setDrawingToolType(_ toolType: String) {
        currentToolType = toolType
        updateCanvasTool()
    }

    private func setEraserTool(eraserType: String) {
        currentToolType = "eraser"
        currentEraserType = eraserType
        updateCanvasTool()
    }

    private func setLassoTool() {
        currentToolType = "lasso"
        updateCanvasTool()
    }

    private func updateCanvasTool() {
        guard let canvasView = canvasView else { return }

        let tool: PKTool

        switch currentToolType.lowercased() {
        case "pen":
            tool = PKInkingTool(.pen, color: currentColor, width: currentWidth)
        case "marker":
            tool = PKInkingTool(.marker, color: currentColor, width: currentWidth)
        case "pencil":
            tool = PKInkingTool(.pencil, color: currentColor, width: currentWidth)
        case "eraser":
            switch currentEraserType.lowercased() {
            case "vector":
                tool = PKEraserTool(.vector, width: currentWidth)  // Add width parameter
            case "bitmap":
                tool = PKEraserTool(.bitmap, width: currentWidth)  // Add width parameter
            default:
                tool = PKEraserTool(.bitmap, width: currentWidth)  // Add width parameter
            }
        case "lasso":
            tool = PKLassoTool()
        default:
            tool = PKInkingTool(.pen, color: currentColor, width: currentWidth)
        }

        canvasView.tool = tool
    }

    // MARK: - Helper Methods

    private func colorFromHexString(_ hexString: String) -> UIColor {
        var hexSanitized = hexString.trimmingCharacters(in: .whitespacesAndNewlines)
        hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")

        var rgb: UInt64 = 0
        Scanner(string: hexSanitized).scanHexInt64(&rgb)

        let red, green, blue, alpha: CGFloat

        if hexSanitized.count <= 6 {
            // RGB format
            red = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
            green = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
            blue = CGFloat(rgb & 0x0000FF) / 255.0
            alpha = 1.0
        } else {
            // RGBA format
            red = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
            green = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
            blue = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
            alpha = CGFloat(rgb & 0x000000FF) / 255.0
        }

        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }

    private func hexStringFromColor(_ color: UIColor) -> String {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0

        color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

        let redInt = Int(red * 255.0)
        let greenInt = Int(green * 255.0)
        let blueInt = Int(blue * 255.0)

        return String(format: "%02X%02X%02X", redInt, greenInt, blueInt)
    }

    // MARK: - Color Picker Delegate Methods

    internal func colorPickerDidFinish(with color: UIColor) {
        canvasView?.backgroundColor = color
    }

    internal func colorPickerDidSelectColor(_ color: UIColor) {
        canvasView?.backgroundColor = color
    }
}

// MARK: - Tool Picker Observer Helper Class

private class ToolPickerObserver: NSObject, PKToolPickerObserver {

    func toolPickerFramesObscuredDidChange(_ toolPicker: PKToolPicker) {
        // Tool picker frames obscured changed
    }

    func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker) {
        // Tool picker visibility changed
    }

    func toolPickerIsRulerActiveDidChange(_ toolPicker: PKToolPicker) {
        // Tool picker ruler active changed
    }

    func toolPickerSelectedToolDidChange(_ toolPicker: PKToolPicker) {
        // Tool picker selected tool changed
    }
}

// MARK: - Color Picker Delegate Helper Class

private class ColorPickerDelegate: NSObject, UIColorPickerViewControllerDelegate {
    weak var module: ExpoPencilKitModule?

    init(module: ExpoPencilKitModule) {
        self.module = module
        super.init()
    }

    func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
        module?.colorPickerDidFinish(with: viewController.selectedColor)
        viewController.dismiss(animated: true)
    }

    func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
        module?.colorPickerDidSelectColor(viewController.selectedColor)
    }
}