using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

[RequireComponent(typeof(Camera))]
public class FreeCameraController : MonoBehaviour
{
    [Header("Rotation")]
    [SerializeField] private float mouseSensitivity = 0.12f;
    [SerializeField] private float minPitch = -80f;
    [SerializeField] private float maxPitch = 80f;

    [Header("Rotation Limits")]
    [Tooltip("Hard left/right limit relative to the starting yaw.")]
    [SerializeField] private float maxYawAngle = 70f;

    [Header("Move (A/D drives local Z)")]
    [SerializeField] private float zMoveSpeed = 4f;

    [Tooltip("Clamp on Z relative to start position, in local space.")]
    [SerializeField] private float minLocalZ = -5f;
    [SerializeField] private float maxLocalZ = 10f;

    [Header("Zoom (FOV)")]
    [Tooltip("Scroll is per tick. Typical range: 1-5.")]
    [SerializeField] private float zoomSpeed = 2.5f;
    [SerializeField] private float minFOV = 30f;
    [SerializeField] private float maxFOV = 75f;

    [Header("Attack Raycast")]
    [SerializeField] private float attackRange = 100f;
    [SerializeField] private LayerMask targetLayerMask;
    [SerializeField] private LayerMask groundLayerMask;

    [Header("Hit Effect")]
    [SerializeField] private GameObject hitEffectPrefab;
    [SerializeField] private GameObject hitGroundPrefab;
    [SerializeField] private float hitEffectLifetime = 2f;

    [Header("Post Processing")]
    [SerializeField] private Volume volume;

    [Header("Audio")]
    [SerializeField] private AudioSource audioSource;
    [SerializeField] private AudioClip shot;

    [Header("CrossAir")]
    [SerializeField] private GameObject cross;

    private Vignette vignette;

    private Camera cam;
    private InputSystem_Actions playerInputActions;

    private Vector2 lookInput;
    private float moveLRInput;  // -1..+1 (A/D)
    private float zoomInput;    // scroll delta (per tick)

    private float yaw;
    private float pitch;
    private float startYaw;
    private Vector3 startLocalPos;

    private void Awake()
    {
        cam = GetComponent<Camera>();

        cross.SetActive(true);

        yaw = transform.eulerAngles.y;
        pitch = transform.eulerAngles.x;
        startYaw = yaw;

        startLocalPos = transform.localPosition;
        playerInputActions = new InputSystem_Actions();

        // 🔒 Lock & hide cursor
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    private void Start()
    {
        // Enable vignette at runtime (optional)
        if (volume != null && volume.profile != null && volume.profile.TryGet(out vignette))
        {
            vignette.active = true;

            // If you want to "force refresh" (usually not needed):
            volume.enabled = false;
            volume.enabled = true;
        }
    }

    private void OnEnable()
    {
        playerInputActions.Player.Enable();

        playerInputActions.Player.Move.performed += OnMoveLR;
        playerInputActions.Player.Move.canceled += OnMoveLR;

        playerInputActions.Player.Look.performed += OnLook;
        playerInputActions.Player.Look.canceled += OnLook;

        playerInputActions.Player.Zoom.performed += OnZoom;
        playerInputActions.Player.Zoom.canceled += OnZoom;

        playerInputActions.Player.Attack.performed += OnAttack;
    }

    private void OnDisable()
    {
        playerInputActions.Player.Move.performed -= OnMoveLR;
        playerInputActions.Player.Move.canceled -= OnMoveLR;

        playerInputActions.Player.Look.performed -= OnLook;
        playerInputActions.Player.Look.canceled -= OnLook;

        playerInputActions.Player.Zoom.performed -= OnZoom;
        playerInputActions.Player.Zoom.canceled -= OnZoom;

        playerInputActions.Player.Attack.performed -= OnAttack;

        playerInputActions.Player.Disable();

        Cursor.lockState = CursorLockMode.None;
        Cursor.visible = true;
    }

    private void Update()
    {
        if (PauseManager.Instance.IsPaused())
        {
            cross.SetActive(false);
            return;
        }
        cross.SetActive(true);
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
        HandleRotationHardLimited();
        HandleMoveZWithLimits();
        HandleZoom();
    }

    private void OnLook(InputAction.CallbackContext ctx) => lookInput = ctx.ReadValue<Vector2>();

    private void OnMoveLR(InputAction.CallbackContext ctx)
    {
        // Move is a 1D Axis (A negative, D positive)
        moveLRInput = ctx.ReadValue<float>();
    }

    private void OnZoom(InputAction.CallbackContext ctx)
    {
        // Zoom binding can be Axis (float) OR Vector2 (mouse scroll delta)
        if (ctx.valueType == typeof(Vector2))
            zoomInput = ctx.ReadValue<Vector2>().y;
        else
            zoomInput = ctx.ReadValue<float>();
    }

    private static bool TryGetSwitch(Component root, out KillSwitch sw)
    {
        if (root.TryGetComponent(out sw)) return true;

        sw = root.GetComponentInParent<KillSwitch>();
        if (sw != null) return true;

        sw = root.GetComponentInChildren<KillSwitch>(true);
        return sw != null;
    }

    private void SpawnGroundEffect(RaycastHit hit)
    {
        if (hitEffectPrefab == null) return;

        Quaternion rot = Quaternion.LookRotation(hit.normal);
        Vector3 pos = hit.point + hit.normal * 0.01f;

        GameObject fx = Instantiate(hitGroundPrefab, pos, rot);

        if (hitEffectLifetime > 0f)
            Destroy(fx, hitEffectLifetime);
    }

    private void SpawnHitEffect(in RaycastHit hit)
    {
        if (hitEffectPrefab == null) return;

        Quaternion rot = Quaternion.LookRotation(hit.normal);
        Vector3 pos = hit.point + hit.normal * 0.01f;

        GameObject fx = Instantiate(hitEffectPrefab, pos, rot);

        if (hitEffectLifetime > 0f)
            Destroy(fx, hitEffectLifetime);
    }

    private void OnAttack(InputAction.CallbackContext ctx)
    {
        if (PauseManager.Instance.IsPaused())
            return;

        Ray ray = cam.ViewportPointToRay(new Vector3(0.35f, 0.5f, 0f));
        audioSource.PlayOneShot(shot);

        bool hitTarget = Physics.Raycast(
            ray,
            out RaycastHit targetHit,
            attackRange,
            targetLayerMask,
            QueryTriggerInteraction.Ignore
        );

        if (hitTarget)
        {
#if UNITY_EDITOR
            Debug.Log($"[Attack] Hit Target: {targetHit.collider.gameObject.name}", targetHit.collider.gameObject);
#endif
            SpawnHitEffect(targetHit);

            if (TryGetSwitch(targetHit.collider, out var switcher))
            {
                switcher.SwitchFromTimelineToAnimator();
            }
#if UNITY_EDITOR
            else
            {
                Debug.LogWarning($"[Attack] No TimelineToAnimatorAutoSwitch found on {targetHit.collider.name} (or parent/children).", targetHit.collider.gameObject);
            }
#endif
        }

        // Always try to spawn a ground effect too (independent layer mask)
        bool hitGround = Physics.Raycast(
            ray,
            out RaycastHit groundHit,
            attackRange,
            groundLayerMask,
            QueryTriggerInteraction.Ignore
        );

        if (hitGround)
        {
#if UNITY_EDITOR
            Debug.Log($"[Attack] Hit Ground: {groundHit.collider.gameObject.name}", groundHit.collider.gameObject);
#endif
            SpawnGroundEffect(groundHit);
        }
#if UNITY_EDITOR
        else if (!hitTarget)
        {
            Debug.Log("[Attack] No Target hit and no Ground hit.");
        }
#endif
    }

    private void HandleRotationHardLimited()
    {
        if (lookInput.sqrMagnitude < 0.0001f) return;

        yaw += lookInput.x * mouseSensitivity;
        pitch -= lookInput.y * mouseSensitivity;

        // Pitch hard clamp
        pitch = Mathf.Clamp(pitch, minPitch, maxPitch);

        // Yaw hard clamp relative to startYaw
        float yawDelta = Mathf.DeltaAngle(startYaw, yaw);
        yawDelta = Mathf.Clamp(yawDelta, -maxYawAngle, maxYawAngle);
        yaw = startYaw + yawDelta;

        transform.rotation = Quaternion.Euler(pitch, yaw, 0f);
    }

    private void HandleMoveZWithLimits()
    {
        if (Mathf.Abs(moveLRInput) < 0.01f) return;

        Vector3 local = transform.localPosition;
        local.z += moveLRInput * zMoveSpeed * Time.deltaTime;

        float relZ = local.z - startLocalPos.z;
        relZ = Mathf.Clamp(relZ, minLocalZ, maxLocalZ);
        local.z = startLocalPos.z + relZ;

        transform.localPosition = local;
    }

    private void HandleZoom()
    {
        if (Mathf.Abs(zoomInput) < 0.01f) return;

        cam.fieldOfView -= zoomInput * zoomSpeed;
        cam.fieldOfView = Mathf.Clamp(cam.fieldOfView, minFOV, maxFOV);

        // prevent scroll from "sticking" depending on action behavior
        zoomInput = 0f;
    }
}
