using UnityEngine;
using UnityEngine.InputSystem;
using Unity.Netcode;
using Unity.Netcode.Components;
using Unity.Cinemachine;

[RequireComponent(typeof(NetworkObject))]
public class PlayerNetworkController : NetworkBehaviour
{
    // ... 기존 변수들 유지 ...
    [Header("Movement Settings")]
    public float moveSpeed = 5f;
    public float rotationSpeed = 10f;
    public float jumpHeight = 1.5f;
    public float gravity = -19.62f;

    [Header("Interaction Settings")]
    [SerializeField] private float interactRange = 3f;
    [SerializeField] private LayerMask interactableLayer;
    [SerializeField] private LayerMask constructionLayer;
    [SerializeField] private float buildSpeedMultiplier = 2f;

    [Header("Mining Settings")]
    [SerializeField] private float attackRange = 3.5f;
    [SerializeField] private float aimRadius = 0.5f;
    [SerializeField] private int miningDamage = 50;
    [SerializeField] private LayerMask mineableLayer;
    [SerializeField] private LayerMask ignoreDuringAim; // 반드시 'Player' 레이어를 포함하세요!

    [Header("Visual Feedback")]
    [SerializeField] private float crosshairScreenRadius = 200f;
    [SerializeField] private UnityEngine.UI.Image crosshairUI;
    [SerializeField] private Sprite idleCrosshair;
    [SerializeField] private Sprite targetCrosshair;
    private RectTransform _crosshairRect;
    private MineableBlock _currentTargetBlock; // 현재 강조 중인 블록 저장

    private CharacterController _controller;
    private PlayerInputActions _inputActions;
    private Animator _animator;
    private TunnelTraveler _traveler;

    private Vector2 _moveInput;
    private Vector3 _velocity;
    private Vector3 _currentMoveDir;
    private bool _isGrounded;
    private bool _isHoldingInteract = false;

    // 디버그 변수
    private Vector3 _debugOrigin;
    private Vector3 _debugDir;
    private bool _debugHit;
    private float _debugDist;

    // NGO 초기화
    public override void OnNetworkSpawn()
    {
        if (!IsOwner) return;

        // 1. 씬에 있는 가상 카메라를 찾습니다.
        // Unity 6에서는 CinemachineVirtualCamera 대신 CinemachineCamera를 주로 사용합니다.
        var vcam = GameObject.FindAnyObjectByType<CinemachineCamera>();

        if (vcam != null)
        {
            // 2. 카메라의 Follow와 LookAt 대상을 '나'로 설정합니다.
            vcam.Follow = transform;
            vcam.LookAt = transform;
            Debug.Log("<color=green>[Camera] 로컬 플레이어에게 카메라가 연결되었습니다.</color>");
        }

        // 씬의 Canvas 안에 있는 "Crosshair"라는 이름의 오브젝트를 찾습니다.
        GameObject crosshairObj = GameObject.Find("Crosshair");
        if (crosshairObj != null)
        {
            _crosshairRect = crosshairObj.GetComponent<RectTransform>();
            crosshairUI = crosshairObj.GetComponent<UnityEngine.UI.Image>();
            // 초기 스프라이트 설정
            crosshairUI.sprite = idleCrosshair;
        }

        _inputActions = new PlayerInputActions();
        _inputActions.Player.Jump.performed += ctx => OnJump();
        _inputActions.Player.Attack.performed += ctx => OnAttackInput();
        _inputActions.Player.Interact.performed += ctx => OnInteractTap(); // 탭 상호작용

        _inputActions.Player.Interact.started += ctx => _isHoldingInteract = true;
        _inputActions.Player.Interact.canceled += ctx => _isHoldingInteract = false;

        _inputActions.Enable();
    }

    void Awake()
    {
        _controller = GetComponent<CharacterController>();
        _animator = GetComponent<Animator>();
        _traveler = GetComponent<TunnelTraveler>();
    }

    void Update()
    {
        if (!IsOwner) return;
        if (_traveler != null && _traveler.IsTraveling) return;

        HandleGravity();
        HandleMovement();

        if (_isHoldingInteract) PerformConstructionSupport();

        UpdateCrosshairPosition(); // 이 안에서 움직여야 합니다.
        UpdateTargetFeedback();
    }

    // --- 이동 관련 로직 (기존 유지) ---
    private void HandleMovement()
    {
        _isGrounded = _controller.isGrounded;
        _animator.SetBool("isGrounded", _isGrounded);

        bool isAttacking = _animator.GetCurrentAnimatorStateInfo(0).IsTag("Attack");
        _moveInput = _inputActions.Player.Move.ReadValue<Vector2>();
        Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized;

        if (isAttacking) move = _isGrounded ? Vector3.zero : _currentMoveDir;
        else if (move.magnitude > 0.1f) _currentMoveDir = move;

        if (move.magnitude >= 0.1f)
        {
            if (!isAttacking)
            {
                Quaternion targetRotation = Quaternion.LookRotation(move);
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
            }
            _controller.Move(move * moveSpeed * Time.deltaTime);
        }
        _animator.SetFloat("MoveSpeed", isAttacking && _isGrounded ? 0 : move.magnitude);
    }

    private void HandleGravity()
    {
        if (_isGrounded && _velocity.y < 0) _velocity.y = -2f;
        _velocity.y += gravity * Time.deltaTime;
        _controller.Move(_velocity * Time.deltaTime);
    }

    private void OnJump()
    {
        if (_isGrounded) _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
    }

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

        // 1. 마우스가 가리키는 월드상의 위치를 먼저 찾습니다.
        Ray mouseRay = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
        Vector3 worldAimPoint;

        // 아주 멀리까지 레이를 쏴서 조준 방향을 결정합니다.
        if (Physics.Raycast(mouseRay, out RaycastHit mouseHit, 1000f, ~ignoreDuringAim))
            worldAimPoint = mouseHit.point;
        else
            worldAimPoint = mouseRay.GetPoint(100f);

        // 2. 캐릭터 가슴 높이에서 조준점을 향하는 방향 계산
        Vector3 origin = transform.position + Vector3.up * 1.2f;
        Vector3 direction = (worldAimPoint - origin).normalized;

        // 3. 캐릭터에서 해당 방향으로 SphereCast를 쏴서 가장 가까운 블록 하나를 찾습니다.
        // SphereCast는 가장 먼저 닿는 오브젝트 하나만 hit에 담습니다.
        if (Physics.SphereCast(origin, aimRadius, direction, out RaycastHit blockHit, attackRange, mineableLayer))
        {
            if (blockHit.collider.TryGetComponent<NetworkObject>(out var netObj))
            {
                // 서버에 대미지 요청
                ApplyMiningDamageServerRpc(netObj.NetworkObjectId);
            }
        }

        _animator.SetTrigger("Attack");
    }

    [Rpc(SendTo.Server)]
    private void ApplyMiningDamageServerRpc(ulong targetId)
    {
        if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(targetId, out var target))
        {
            if (target.TryGetComponent<MineableBlock>(out var block))
            {
                // 서버에서 최종 거리 검증 후 대미지 적용
                if (Vector3.Distance(transform.position, target.transform.position) <= attackRange + 1.0f)
                {
                    block.TakeDamageRpc(miningDamage);
                    block.PlayHitEffectClientRpc();
                }
            }
        }
    }

    [ClientRpc]
    private void OnAttackClientRpc() => _animator.SetTrigger("Attack");

    private void OnInteractTap()
    {
        if (!IsOwner) return;
        _animator.SetTrigger("Interact");

        // 1. 캐릭터 주변 반경 내의 모든 콜라이더 감지
        Collider[] colliders = Physics.OverlapSphere(transform.position, interactRange, interactableLayer);

        IInteractable closestTarget = null;
        float minDistance = float.MaxValue;

        foreach (var col in colliders)
        {
            // 2. 방향 조건 없이 거리만 체크하여 가장 가까운 것 선택
            float dist = Vector3.Distance(transform.position, col.transform.position);
            if (dist < minDistance)
            {
                IInteractable interactable = col.GetComponentInParent<IInteractable>();
                if (interactable != null)
                {
                    minDistance = dist;
                    closestTarget = interactable;
                }
            }
        }

        // 3. 가장 가까운 대상이 있다면 상호작용 실행
        if (closestTarget != null)
        {
            closestTarget.Interact(gameObject);
        }
    }

    // 건설 지원 로직 (범위 내 지속 작업이므로 OverlapSphere 유지 또는 Raycast로 변경 가능)
    private void PerformConstructionSupport()
    {
        Collider[] targets = Physics.OverlapSphere(transform.position, interactRange, constructionLayer);
        foreach (var col in targets)
        {
            ConstructionSite site = col.GetComponentInParent<ConstructionSite>();
            if (site != null)
            {
                site.AdvanceConstruction(Time.deltaTime * buildSpeedMultiplier);
            }
        }
    }

    public override void OnNetworkDespawn()
    {
        if (IsOwner && _inputActions != null) _inputActions.Disable();
    }

    private void UpdateCrosshairPosition()
    {
        // 1. 변수 할당 확인 (할당이 안 되어 있으면 여기서 찾음)
        if (_crosshairRect == null)
        {
            GameObject go = GameObject.Find("Crosshair");
            if (go != null) _crosshairRect = go.GetComponent<RectTransform>();
            else return; // 여전히 없으면 중단
        }

        // 2. 마우스 입력 읽기 (New Input System)
        Vector2 mousePos = Mouse.current.position.ReadValue();
        Vector2 screenCenter = new Vector2(Screen.width / 2f, Screen.height / 2f);

        // 3. 중앙으로부터의 거리 계산
        Vector2 offset = mousePos - screenCenter;

        // 4. [중요] 반지름 제한 확인 (crosshairScreenRadius가 0이면 이동하지 않음)
        if (crosshairScreenRadius > 0)
        {
            // 내적과 크기 계산을 통해 원형 제한 적용
            if (offset.magnitude > crosshairScreenRadius)
            {
                offset = offset.normalized * crosshairScreenRadius;
            }
        }

        // 5. UI 좌표 적용 (Screen Space - Overlay 기준)
        _crosshairRect.position = screenCenter + offset;
    }
    private void UpdateTargetFeedback()
    {
        if (!IsOwner || _crosshairRect == null) return;

        // 1. [조준점 확보] 카메라 레이로 마우스가 가리키는 '실제 지점'을 찾습니다.
        Ray cameraRay = Camera.main.ScreenPointToRay(_crosshairRect.position);
        RaycastHit cameraHit;

        // 지형이나 블록을 모두 검사하여 조준점을 잡습니다.
        bool hitSomething = Physics.Raycast(cameraRay, out cameraHit, 150f, ~ignoreDuringAim);
        Vector3 worldAimPoint = hitSomething ? cameraHit.point : cameraRay.GetPoint(100f);

        // 2. [거리 및 방향 계산] 캐릭터 가슴에서 그 지점까지의 벡터를 구합니다.
        Vector3 origin = transform.position + Vector3.up * 1.2f;
        Vector3 toTarget = worldAimPoint - origin;
        float distToTarget = toTarget.magnitude;
        Vector3 direction = toTarget.normalized;

        // 3. [대상 우선 판정] 
        // 만약 카메라 레이가 '채광 가능한 블록'을 직접 때렸고, 그게 사거리 이내라면? -> 바로 타겟팅!
        bool isDirectHit = hitSomething && ((1 << cameraHit.collider.gameObject.layer) & mineableLayer) != 0;

        RaycastHit finalHit;
        bool hasValidTarget = false;

        if (isDirectHit && distToTarget <= attackRange)
        {
            // 마우스가 직접 블록을 가리키고 사거리 내에 있는 경우
            finalHit = cameraHit;
            hasValidTarget = true;
        }
        else
        {
            // 마우스가 허공을 보거나 너무 먼 곳을 볼 때만 '범위 탐색(SphereCast)'을 수행합니다.
            float searchDist = Mathf.Min(distToTarget, attackRange);
            Vector3 rayStart = origin + direction * 0.4f;
            hasValidTarget = Physics.SphereCast(rayStart, aimRadius, direction, out finalHit, searchDist - 0.4f, mineableLayer);
        }

        // 4. 디버그 및 시각화 업데이트
        _debugOrigin = origin;
        _debugDir = direction;
        _debugHit = hasValidTarget;
        _debugDist = hasValidTarget ? Vector3.Distance(origin, finalHit.point) : Mathf.Min(distToTarget, attackRange);

        UpdateBlockVisuals(hasValidTarget, finalHit);
    }

    private void UpdateBlockVisuals(bool hasTarget, RaycastHit hit)
    {
        MineableBlock newTarget = null;

        if (hasTarget)
        {
            // 부모나 자신에게서 MineableBlock 컴포넌트를 찾습니다.
            newTarget = hit.collider.GetComponentInParent<MineableBlock>();
        }

        // 대상이 바뀌었을 때만 실행 (최적화)
        if (_currentTargetBlock != newTarget)
        {
            // 1. 이전 타겟 하이라이트 해제
            if (_currentTargetBlock != null)
            {
                _currentTargetBlock.SetHighlight(false);
            }

            // 2. 새로운 타겟 하이라이트 적용
            if (newTarget != null)
            {
                newTarget.SetHighlight(true);
            }

            _currentTargetBlock = newTarget;
        }

        // 3. 크로스헤어 상태 업데이트
        if (crosshairUI != null)
        {
            crosshairUI.sprite = hasTarget ? targetCrosshair : idleCrosshair;
            crosshairUI.color = hasTarget ? Color.green : Color.white;
        }
    }

    private void OnDrawGizmos()
    {
        if (!Application.isPlaying || !IsOwner) return;

        // 실제 채굴 탐색 궤적을 씬 뷰에 표시
        Gizmos.color = _debugHit ? Color.red : Color.green;

        // 광선 표시
        Gizmos.DrawLine(_debugOrigin, _debugOrigin + _debugDir * _debugDist);

        // 탐색 영역(구체) 표시
        Gizmos.DrawWireSphere(_debugOrigin + _debugDir * _debugDist, aimRadius);
    }
}