import UIKit
import ARKit
import React

class ARView: UIView, ARSessionDelegate, ARSCNViewDelegate {
    
    // MARK: - Properties
    
    private var sceneView: ARSCNView!
    private var session: ARSession {
        return sceneView.session
    }
    private var planes: [String: ARPlaneAnchor] = [:]
    private var objects: [String: SCNNode] = [:]
    
    // Public properties exposed as React props
    var placeOnTap: Bool = true
    var objectScale: Float = 1.0
    
    // Event callbacks
    @objc var onTrackingUpdatedNative: RCTDirectEventBlock?
    @objc var onPlaneDetectedNative: RCTDirectEventBlock?
    @objc var onObjectPlacedNative: RCTDirectEventBlock?
    @objc var onTapNative: RCTDirectEventBlock?
    
    // MARK: - Initialization
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupARView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupARView()
    }
    
    private func setupARView() {
        // Create AR scene view
        sceneView = ARSCNView(frame: bounds)
        sceneView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        sceneView.delegate = self
        addSubview(sceneView)
        
        // Set session delegate
        sceneView.session.delegate = self
        
        // Add tap gesture recognizer
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        sceneView.addGestureRecognizer(tapGesture)
        
        // Configure lighting
        sceneView.automaticallyUpdatesLighting = true
        sceneView.scene.lightingEnvironment.intensity = 1.0
        
        // Start AR session
        startARSession()
    }
    
    private func startARSession() {
        // Create configuration
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = [.horizontal, .vertical]
        
        // Run session
        sceneView.session.run(configuration)
    }
    
    // MARK: - User Interactions
    
    @objc private func handleTap(_ gesture: UITapGestureRecognizer) {
        let location = gesture.location(in: sceneView)
        
        // Perform hit test
        let hitTestResults = sceneView.hitTest(location, types: [.existingPlaneUsingExtent, .featurePoint])
        
        if let hitResult = hitTestResults.first {
            // Get hit position
            let position = SCNVector3(
                hitResult.worldTransform.columns.3.x,
                hitResult.worldTransform.columns.3.y,
                hitResult.worldTransform.columns.3.z
            )
            
            // Create event data for tap
            var eventBody: [String: Any] = [
                "position": [
                    "x": Double(position.x),
                    "y": Double(position.y),
                    "z": Double(position.z)
                ]
            ]
            
            // Check if we hit a plane
            if let planeAnchor = hitResult.anchor as? ARPlaneAnchor {
                let planeId = getPlaneId(planeAnchor)
                eventBody["planeId"] = planeId
            }
            
            // Check if we hit an object
            let objectHitResults = sceneView.hitTest(location, options: [.searchMode: SCNHitTestSearchMode.all.rawValue])
            if let objectHit = objectHitResults.first, let objectNode = objectHit.node.parent, let objectId = getObjectId(objectNode) {
                eventBody["objectId"] = objectId
            }
            
            // Send tap event
            onTapNative?(eventBody)
            
            // Place object if enabled
            if placeOnTap {
                placeObject(at: position)
            }
        }
    }
    
    private func placeObject(at position: SCNVector3) {
        // Create a cube node as a simple placeholder for demo
        let cubeNode = SCNNode(geometry: SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.01))
        cubeNode.position = position
        cubeNode.scale = SCNVector3(objectScale, objectScale, objectScale)
        
        // Set material
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.blue
        material.specular.contents = UIColor.white
        cubeNode.geometry?.materials = [material]
        
        // Add to scene
        sceneView.scene.rootNode.addChildNode(cubeNode)
        
        // Generate unique ID
        let objectId = UUID().uuidString
        objects[objectId] = cubeNode
        
        // Create event data
        let eventBody: [String: Any] = [
            "id": objectId,
            "position": [
                "x": Double(position.x),
                "y": Double(position.y),
                "z": Double(position.z)
            ],
            "scale": Double(objectScale),
            "rotation": [
                "x": 0.0,
                "y": 0.0,
                "z": 0.0
            ]
        ]
        
        // Send object placed event
        onObjectPlacedNative?(eventBody)
    }
    
    // MARK: - ARSessionDelegate
    
    func session(_ session: ARSession, didUpdate frame: ARFrame) {
        // Update tracking state
        let trackingState = frame.camera.trackingState
        updateTrackingState(trackingState)
    }
    
    func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        for anchor in anchors {
            if let planeAnchor = anchor as? ARPlaneAnchor {
                addPlane(planeAnchor)
            }
        }
    }
    
    func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
        for anchor in anchors {
            if let planeAnchor = anchor as? ARPlaneAnchor {
                updatePlane(planeAnchor)
            }
        }
    }
    
    func session(_ session: ARSession, didRemove anchors: [ARAnchor]) {
        for anchor in anchors {
            if let planeAnchor = anchor as? ARPlaneAnchor {
                removePlane(planeAnchor)
            }
        }
    }
    
    // MARK: - Plane Management
    
    private func addPlane(_ planeAnchor: ARPlaneAnchor) {
        let planeId = getPlaneId(planeAnchor)
        planes[planeId] = planeAnchor
        
        // Create visualization for plane (in a real app)
        // This would create a visual representation of the detected plane
        
        // Notify about the detected plane
        notifyPlaneDetected(planeAnchor)
    }
    
    private func updatePlane(_ planeAnchor: ARPlaneAnchor) {
        let planeId = getPlaneId(planeAnchor)
        planes[planeId] = planeAnchor
        
        // Update plane visualization (in a real app)
    }
    
    private func removePlane(_ planeAnchor: ARPlaneAnchor) {
        let planeId = getPlaneId(planeAnchor)
        planes.removeValue(forKey: planeId)
        
        // Remove plane visualization (in a real app)
    }
    
    private func notifyPlaneDetected(_ planeAnchor: ARPlaneAnchor) {
        let planeId = getPlaneId(planeAnchor)
        let position = planeAnchor.transform.columns.3
        
        let orientation: String
        if #available(iOS 11.3, *) {
            orientation = planeAnchor.classification == .vertical ? "VERTICAL" : "HORIZONTAL"
        } else {
            orientation = "HORIZONTAL"
        }
        
        // Create event data
        let eventBody: [String: Any] = [
            "id": planeId,
            "position": [
                "x": Double(position.x),
                "y": Double(position.y),
                "z": Double(position.z)
            ],
            "extent": [
                "width": Double(planeAnchor.extent.x),
                "height": Double(planeAnchor.extent.z)
            ],
            "orientation": orientation
        ]
        
        // Send plane detected event
        onPlaneDetectedNative?(eventBody)
    }
    
    // MARK: - Helpers
    
    private func getPlaneId(_ planeAnchor: ARPlaneAnchor) -> String {
        return "plane_\(planeAnchor.identifier.uuidString)"
    }
    
    private func getObjectId(_ node: SCNNode) -> String? {
        for (id, objectNode) in objects {
            if objectNode === node {
                return id
            }
        }
        return nil
    }
    
    private func updateTrackingState(_ trackingState: ARCamera.TrackingState) {
        let state: String
        let reason: String
        
        switch trackingState {
        case .normal:
            state = "NORMAL"
            reason = "NONE"
        case .notAvailable:
            state = "NOT_AVAILABLE"
            reason = "NONE"
        case .limited(let trackingReason):
            state = "LIMITED"
            switch trackingReason {
            case .initializing:
                reason = "INITIALIZING"
            case .excessiveMotion:
                reason = "EXCESSIVE_MOTION"
            case .insufficientFeatures:
                reason = "INSUFFICIENT_FEATURES"
            case .relocalizing:
                reason = "RELOCALIZING"
            @unknown default:
                reason = "NONE"
            }
        @unknown default:
            state = "NOT_AVAILABLE"
            reason = "NONE"
        }
        
        // Create event data
        let eventBody: [String: Any] = [
            "state": state,
            "reason": reason
        ]
        
        // Send tracking state update event
        onTrackingUpdatedNative?(eventBody)
    }
}
