using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;

namespace Northbound
{
    public class BuildingPlacement : NetworkBehaviour
    {
        [Header("Settings")]
        public LayerMask groundLayer;
        public float maxPlacementDistance = 100f;

        [Header("Preview Materials")]
        public Material validMaterial;
        public Material invalidMaterial;

        [Header("Current Selection")]
        public int selectedBuildingIndex = 0;

        [Header("Debug Visualization")]
        public bool showGridBounds = true;

        private bool isBuildModeActive = false;
        private GameObject previewObject;
        private int currentRotation = 0; // 0-3
        private Material[] originalMaterials;
        private Renderer[] previewRenderers;
        private PlayerInputActions _inputActions;

        public override void OnNetworkSpawn()
        {
            if (!IsOwner) return;

            _inputActions = new PlayerInputActions();
            _inputActions.Player.ToggleBuildMode.performed += OnToggleBuildMode;
            _inputActions.Player.Rotate.performed += OnRotate;
            _inputActions.Player.Build.performed += OnBuild;
            _inputActions.Enable();

            // Create default materials if not assigned
            if (validMaterial == null)
            {
                validMaterial = CreateGhostMaterial(new Color(0, 1, 0, 0.5f));
            }

            if (invalidMaterial == null)
            {
                invalidMaterial = CreateGhostMaterial(new Color(1, 0, 0, 0.5f));
            }
        }

        private Material CreateGhostMaterial(Color color)
        {
            // Try to find appropriate shader for current render pipeline
            Shader shader = Shader.Find("Universal Render Pipeline/Lit");
            if (shader == null)
                shader = Shader.Find("Standard");
            if (shader == null)
                shader = Shader.Find("Diffuse");

            Material mat = new Material(shader);

            // Set base color
            if (mat.HasProperty("_BaseColor"))
                mat.SetColor("_BaseColor", color); // URP
            else if (mat.HasProperty("_Color"))
                mat.SetColor("_Color", color); // Standard

            // Enable transparency
            if (mat.HasProperty("_Surface"))
            {
                // URP Transparency
                mat.SetFloat("_Surface", 1); // Transparent
                mat.SetFloat("_Blend", 0); // Alpha
                mat.SetFloat("_AlphaClip", 0);
                mat.SetFloat("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                mat.SetFloat("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                mat.SetFloat("_ZWrite", 0);
                mat.renderQueue = 3000;
                mat.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
                mat.EnableKeyword("_ALPHAPREMULTIPLY_ON");
            }
            else
            {
                // Standard RP Transparency
                mat.SetFloat("_Mode", 3); // Transparent mode
                mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                mat.SetInt("_ZWrite", 0);
                mat.DisableKeyword("_ALPHATEST_ON");
                mat.EnableKeyword("_ALPHABLEND_ON");
                mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                mat.renderQueue = 3000;
            }

            return mat;
        }

        public override void OnNetworkDespawn()
        {
            if (IsOwner && _inputActions != null)
            {
                _inputActions.Player.ToggleBuildMode.performed -= OnToggleBuildMode;
                _inputActions.Player.Rotate.performed -= OnRotate;
                _inputActions.Player.Build.performed -= OnBuild;
                _inputActions.Disable();
                _inputActions.Dispose();
            }
        }

        private void Update()
        {
            if (!IsOwner) return;

            if (isBuildModeActive)
            {
                UpdatePreviewPosition();
            }
        }

        private void OnDrawGizmos()
        {
            if (!IsOwner || !isBuildModeActive || !showGridBounds) return;
            if (BuildingManager.Instance == null || previewObject == null || !previewObject.activeSelf) return;

            BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
            if (data == null) return;

            // Draw grid bounds being used for collision detection
            Bounds bounds = BuildingManager.Instance.GetPlacementBounds(data, previewObject.transform.position, currentRotation);

            // Check if valid
            bool isValid = BuildingManager.Instance.IsValidPlacement(data, previewObject.transform.position, currentRotation, out _);

            Gizmos.color = isValid ? new Color(0, 1, 0, 0.5f) : new Color(1, 0, 0, 0.5f);
            Gizmos.DrawWireCube(bounds.center, bounds.size);

            // Draw grid cells
            Gizmos.color = Color.yellow;
            float gridSize = BuildingManager.Instance.gridSize;
            Vector3 snappedPos = BuildingManager.Instance.SnapToGrid(previewObject.transform.position);

            // Draw grid origin point
            Gizmos.DrawSphere(snappedPos, 0.1f);

            // Draw grid outline for each cell
            int width = Mathf.RoundToInt(bounds.size.x / gridSize);
            int length = Mathf.RoundToInt(bounds.size.z / gridSize);

            for (int x = 0; x < width; x++)
            {
                for (int z = 0; z < length; z++)
                {
                    Vector3 cellPos = snappedPos + new Vector3(
                        (x - width / 2f + 0.5f) * gridSize,
                        0.01f,
                        (z - length / 2f + 0.5f) * gridSize
                    );
                    Gizmos.DrawWireCube(cellPos, new Vector3(gridSize, 0.01f, gridSize));
                }
            }
        }

        private void OnToggleBuildMode(InputAction.CallbackContext context)
        {
            ToggleBuildMode();
        }

        private void OnRotate(InputAction.CallbackContext context)
        {
            if (!isBuildModeActive) return;

            float rotateValue = context.ReadValue<float>();
            if (rotateValue > 0)
            {
                currentRotation = (currentRotation + 1) % 4;
            }
            else if (rotateValue < 0)
            {
                currentRotation = (currentRotation - 1 + 4) % 4;
            }
        }

        private void OnBuild(InputAction.CallbackContext context)
        {
            if (!isBuildModeActive) return;
            TryPlaceBuilding();
        }

        private void ToggleBuildMode()
        {
            isBuildModeActive = !isBuildModeActive;

            if (isBuildModeActive)
            {
                EnterBuildMode();
            }
            else
            {
                ExitBuildMode();
            }
        }

        private void EnterBuildMode()
        {
            currentRotation = 0;
            CreatePreview();
        }

        private void ExitBuildMode()
        {
            DestroyPreview();
        }

        private void CreatePreview()
        {
            if (BuildingManager.Instance == null)
                return;

            if (BuildingManager.Instance.availableBuildings.Count == 0)
                return;

            selectedBuildingIndex = Mathf.Clamp(selectedBuildingIndex, 0,
                BuildingManager.Instance.availableBuildings.Count - 1);

            BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
            if (data == null || data.prefab == null)
                return;

            previewObject = Instantiate(data.prefab);
            previewObject.name = "BuildingPreview";

            // Disable colliders on preview
            foreach (var collider in previewObject.GetComponentsInChildren<Collider>())
            {
                collider.enabled = false;
            }

            // Remove NetworkObject from preview if exists
            NetworkObject netObj = previewObject.GetComponent<NetworkObject>();
            if (netObj != null)
            {
                Destroy(netObj);
            }

            // Store original materials and setup preview renderers
            previewRenderers = previewObject.GetComponentsInChildren<Renderer>();

            if (previewRenderers.Length == 0)
            {
                // Add a debug cube so you can at least see something
                GameObject debugCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                debugCube.transform.SetParent(previewObject.transform);
                debugCube.transform.localPosition = Vector3.zero;
                Destroy(debugCube.GetComponent<Collider>());
                previewRenderers = previewObject.GetComponentsInChildren<Renderer>();
            }

            originalMaterials = new Material[previewRenderers.Length];

            for (int i = 0; i < previewRenderers.Length; i++)
            {
                originalMaterials[i] = previewRenderers[i].material;
                // Replace all materials with ghost material
                Material[] mats = new Material[previewRenderers[i].materials.Length];
                for (int j = 0; j < mats.Length; j++)
                {
                    mats[j] = validMaterial;
                }
                previewRenderers[i].materials = mats;

                // Disable shadow casting
                previewRenderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
                previewRenderers[i].receiveShadows = false;
            }
        }

        private void DestroyPreview()
        {
            if (previewObject != null)
            {
                Destroy(previewObject);
                previewObject = null;
            }
        }

        private void UpdatePreviewPosition()
        {
            if (BuildingManager.Instance == null) return;

            if (previewObject == null)
            {
                CreatePreview();
                if (previewObject == null) return;
            }

            if (Mouse.current == null)
                return;

            Vector2 mousePos = Mouse.current.position.ReadValue();
            Ray ray = Camera.main.ScreenPointToRay(mousePos);

            Vector3 targetPosition = Vector3.zero;
            bool foundPosition = false;

            // Robust approach: Find where ray intersects ground plane, then raycast down from there
            // This works even when pointing at empty space between buildings

            // Step 1: Calculate where ray intersects a horizontal plane at Y=0
            // Using plane intersection math
            Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
            float enter;

            if (groundPlane.Raycast(ray, out enter))
            {
                // Get the XZ position where ray hits the ground plane
                Vector3 planeHitPoint = ray.GetPoint(enter);

                // Step 2: Cast down from above this XZ position to find actual ground
                Vector3 highPoint = new Vector3(planeHitPoint.x, 100f, planeHitPoint.z);

                if (Physics.Raycast(highPoint, Vector3.down, out RaycastHit downHit, 200f, groundLayer))
                {
                    // Always use the hit point
                    targetPosition = downHit.point;
                    foundPosition = true;
                }
                else
                {
                    // No ground found via raycast, use the plane intersection point directly
                    targetPosition = planeHitPoint;
                    foundPosition = true;
                }
            }
            else
            {
                // Fallback: Try direct raycast to anything
                if (Physics.Raycast(ray, out RaycastHit anyHit, maxPlacementDistance))
                {
                    targetPosition = anyHit.point;
                    foundPosition = true;
                }
            }

            if (foundPosition)
            {
                BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];

                // IsValidPlacement now returns the snapped position in groundPosition
                bool isValid = BuildingManager.Instance.IsValidPlacement(data, targetPosition, currentRotation, out Vector3 snappedPosition);

                previewObject.transform.position = snappedPosition + data.placementOffset;
                previewObject.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);

                if (!previewObject.activeSelf)
                {
                    previewObject.SetActive(true);
                }

                // Update material
                Material previewMat = isValid ? validMaterial : invalidMaterial;
                foreach (var renderer in previewRenderers)
                {
                    Material[] mats = new Material[renderer.materials.Length];
                    for (int i = 0; i < mats.Length; i++)
                    {
                        mats[i] = previewMat;
                    }
                    renderer.materials = mats;
                }
            }
            else
            {
                if (previewObject.activeSelf)
                {
                    previewObject.SetActive(false);
                }
            }
        }

        private void TryPlaceBuilding()
        {
            if (previewObject == null || !previewObject.activeSelf) return;

            Vector2 mousePos = Mouse.current.position.ReadValue();
            Ray ray = Camera.main.ScreenPointToRay(mousePos);

            Vector3 targetPosition = Vector3.zero;
            bool foundPosition = false;

            // Use plane intersection to get XZ position, then raycast down to find ground
            Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
            float enter;

            if (groundPlane.Raycast(ray, out enter))
            {
                Vector3 planeHitPoint = ray.GetPoint(enter);
                Vector3 highPoint = new Vector3(planeHitPoint.x, 100f, planeHitPoint.z);

                if (Physics.Raycast(highPoint, Vector3.down, out RaycastHit downHit, 200f, groundLayer))
                {
                    if (downHit.collider.GetComponentInParent<Building>() == null)
                    {
                        targetPosition = downHit.point;
                        foundPosition = true;
                    }
                    else
                    {
                        targetPosition = downHit.point;
                        foundPosition = true;
                    }
                }
                else
                {
                    targetPosition = planeHitPoint;
                    foundPosition = true;
                }
            }
            else
            {
                // Fallback: direct raycast
                if (Physics.Raycast(ray, out RaycastHit anyHit, maxPlacementDistance))
                {
                    targetPosition = anyHit.point;
                    foundPosition = true;
                }
            }

            if (foundPosition)
            {
                BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];

                // IsValidPlacement now returns the snapped position
                if (BuildingManager.Instance.IsValidPlacement(data, targetPosition, currentRotation, out Vector3 snappedPosition))
                {
                    BuildingManager.Instance.PlaceBuildingServerRpc(selectedBuildingIndex, snappedPosition, currentRotation);
                }
            }
        }


        private void OnDestroy()
        {
            DestroyPreview();

            if (_inputActions != null)
            {
                _inputActions.Dispose();
            }
        }
    }
}
