using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;


public enum SinglePlayerCameraMode
{
    Stationary,
    FollowAndOrbit
}

public class CameraManager : Singleton<CameraManager>
{
    [Header("Component References")]
    public GameObject gameplayCameraObject;
    public GameObject uiOverlayCameraObject;

    [Header("Virtual Cameras")]
    public GameObject VCamStationaryObject;
    public GameObject VCamSinglePlayerOrbitObject;
    
    [Header("Cinemachine References")]
    [SerializeField] private CinemachineFreeLook mainFreeLookCamera;
    [SerializeField] private CinemachineVirtualCamera mainVirtualCamera;
    
    [Header("Current Target")]
    [SerializeField] private TeamMember currentTarget;
    [SerializeField] private bool debugLogging = true;

    public void SetupManager()
    {
        SetCameraObjectNewState(gameplayCameraObject, true);
        SetCameraObjectNewState(uiOverlayCameraObject, false);
        
        // Find camera references if not set
        FindCameraReferences();
    }
    
    private void Start()
    {
        FindCameraReferences();
        SubscribeToAllTeamMembers();
    }
    
    private void FindCameraReferences()
    {
        if (mainFreeLookCamera == null)
        {
            GameObject camObj = GameObject.Find("CinemachineCamera");
            if (camObj != null)
            {
                mainFreeLookCamera = camObj.GetComponent<CinemachineFreeLook>();
            }
        }
        
        if (mainVirtualCamera == null)
        {
            mainVirtualCamera = FindObjectOfType<CinemachineVirtualCamera>();
        }
    }

    public void SetupSinglePlayerCamera(SinglePlayerCameraMode currentCameraMode)
    {
        switch (currentCameraMode)
        {
            case SinglePlayerCameraMode.Stationary:
                SetCameraObjectNewState(VCamStationaryObject, true);
                SetCameraObjectNewState(VCamSinglePlayerOrbitObject, false);
                break;

            case SinglePlayerCameraMode.FollowAndOrbit:
                SetCameraObjectNewState(VCamStationaryObject, false);
                SetCameraObjectNewState(VCamSinglePlayerOrbitObject, true);
                break;
        }
    }

    void SetCameraObjectNewState(GameObject cameraObject, bool newState)
    {
        if (cameraObject != null)
            cameraObject.SetActive(newState);
    }

    //This is called by UIBillboardBehaviour so they can orient to wherever the gameplay camera is.
    public Transform GetGameplayCameraTransform()
    {
        return gameplayCameraObject?.transform;
    }

    public Camera GetGameplayCamera()
    {
        return gameplayCameraObject?.GetComponent<Camera>();
    }
    
    // ==================== NEW: ControlType-aware Camera Targeting ====================
    
    /// <summary>
    /// Subscribe to OnControlTypeChanged for all TeamMembers in the scene
    /// </summary>
    private void SubscribeToAllTeamMembers()
    {
        TeamMember[] allMembers = FindObjectsOfType<TeamMember>();
        foreach (var member in allMembers)
        {
            member.OnControlTypeChanged += OnMemberControlTypeChanged;
        }
    }
    
    /// <summary>
    /// Register a newly spawned TeamMember
    /// </summary>
    public void RegisterTeamMember(TeamMember member)
    {
        if (member != null)
        {
            member.OnControlTypeChanged += OnMemberControlTypeChanged;
            
            // If this member is HumanControlled, set as target
            if (member.CurrentControlType == TeamMember.ControlType.HumanControlled)
            {
                SetTarget(member);
            }
        }
    }
    
    /// <summary>
    /// Unregister a TeamMember (e.g., on death)
    /// </summary>
    public void UnregisterTeamMember(TeamMember member)
    {
        if (member != null)
        {
            member.OnControlTypeChanged -= OnMemberControlTypeChanged;
        }
    }
    
    /// <summary>
    /// Called when any TeamMember's control type changes
    /// </summary>
    private void OnMemberControlTypeChanged(TeamMember.ControlType newType)
    {
        // Find the new human-controlled character
        if (newType == TeamMember.ControlType.HumanControlled)
        {
            FindAndSetHumanControlledTarget();
        }
    }
    
    /// <summary>
    /// Find the HumanControlled character and set as camera target
    /// </summary>
    public void FindAndSetHumanControlledTarget()
    {
        TeamMember[] allMembers = FindObjectsOfType<TeamMember>();
        foreach (var member in allMembers)
        {
            if (member.CurrentControlType == TeamMember.ControlType.HumanControlled)
            {
                SetTarget(member);
                return;
            }
        }
        
        if (debugLogging)
        {
            Debug.LogWarning("[CameraManager] No HumanControlled character found in scene!");
        }
    }
    
    /// <summary>
    /// Set the camera target to a specific TeamMember
    /// </summary>
    public void SetTarget(TeamMember target)
    {
        if (target == null)
        {
            Debug.LogWarning("[CameraManager] Attempted to set null target");
            return;
        }
        
        currentTarget = target;
        
        // Use root for Follow (stable orbit center)
        Transform followTransform = target.transform;
        // Use head for LookAt (camera points at upper body)
        Transform lookAtTransform = FindCharacterHead(target.gameObject);
        
        // Set FreeLook camera target
        if (mainFreeLookCamera != null)
        {
            mainFreeLookCamera.Follow = followTransform;
            mainFreeLookCamera.LookAt = lookAtTransform;
            
            // CRITICAL: Tell Cinemachine to forget its cached (underground) position
            // and recalculate from scratch based on the new Follow target position
            mainFreeLookCamera.PreviousStateIsValid = false;
            
            // Configure camera to automatically stay behind player
            ConfigureCameraRecentering();
            
            if (debugLogging)
            {
                Debug.Log($"[CameraManager] FreeLook camera - Follow: {followTransform.name}, LookAt: {lookAtTransform.name} (state reset)");
            }
        }
        
        // Set VirtualCamera target
        if (mainVirtualCamera != null)
        {
            mainVirtualCamera.Follow = followTransform;
            mainVirtualCamera.LookAt = lookAtTransform;
        }
        
        // Also update CameraFollow if present
        CameraFollow cameraFollow = FindObjectOfType<CameraFollow>();
        if (cameraFollow != null)
        {
            cameraFollow.target = lookAtTransform;
        }
    }
    
    /// <summary>
    /// Configure camera to automatically recenter behind the player.
    /// Also initializes orbit heights, Y axis position, and Composer aim settings
    /// to ensure the camera starts BEHIND and ABOVE the character, never below.
    /// </summary>
    private void ConfigureCameraRecentering()
    {
        if (mainFreeLookCamera == null) return;
        
        // ====== FIX #1: Start at MIDDLE rig (0.5), not bottom rig (0) ======
        // Y Axis: 0 = bottom rig, 0.5 = middle rig, 1 = top rig
        // Without this, camera defaults to bottom rig and looks UP at the character
        mainFreeLookCamera.m_YAxis.Value = 0.5f;
        
        // CRITICAL: Clear default input axis names — CinemachineCameraInput handles all input.
        // Without this, Cinemachine also reads Input.GetAxis("Mouse X") which returns 0
        // and snaps the Y axis to the bottom rig (= camera underground).
        mainFreeLookCamera.m_XAxis.m_InputAxisName = "";
        mainFreeLookCamera.m_YAxis.m_InputAxisName = "";
        
        // ====== FIX #2: Set proper orbit heights and radii ======
        // These define the 3 orbital rings the camera blends between.
        // Height = vertical offset from Follow target (character root)
        // Radius = horizontal distance from character
        mainFreeLookCamera.m_Orbits = new CinemachineFreeLook.Orbit[] {
            new CinemachineFreeLook.Orbit(4.5f, 5f),   // TopRig:    bird's eye, medium distance
            new CinemachineFreeLook.Orbit(2.5f, 6f),   // MiddleRig: shoulder height, wider orbit
            new CinemachineFreeLook.Orbit(0.5f, 5f),   // BottomRig: just above ground level
        };
        
        // Set binding mode to follow player's heading (critical for auto-follow)
        // "SimpleFollowWithWorldUp" makes the camera orbit relative to where the player is facing
        mainFreeLookCamera.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp;
        
        // Configure Heading - this is how the camera knows which direction is "behind" the player
        // Velocity-based heading follows the direction the player is moving
        mainFreeLookCamera.m_Heading.m_Definition = CinemachineOrbitalTransposer.Heading.HeadingDefinition.Velocity;
        mainFreeLookCamera.m_Heading.m_VelocityFilterStrength = 5; // Smooth out velocity changes
        
        // Try to enable X Axis recentering (works in Cinemachine 2.x)
        try
        {
            mainFreeLookCamera.m_XAxis.m_Recentering.m_enabled = true;
            mainFreeLookCamera.m_XAxis.m_Recentering.m_WaitTime = 0.3f;
            mainFreeLookCamera.m_XAxis.m_Recentering.m_RecenteringTime = 0.8f;
        }
        catch (System.Exception)
        {
            Debug.Log("[CameraManager] X Axis recentering not available via m_XAxis.m_Recentering");
        }
        
        // Configure Y Axis (vertical tilt) recentering
        mainFreeLookCamera.m_YAxis.m_Recentering.m_enabled = true;
        mainFreeLookCamera.m_YAxis.m_Recentering.m_WaitTime = 0.3f;
        mainFreeLookCamera.m_YAxis.m_Recentering.m_RecenteringTime = 0.8f;
        
        // ====== FIX #3: Configure Composer aim on each rig ======
        // This controls WHERE on screen the character appears.
        // ScreenY = 0.4 means character is at 40% from bottom (slightly above center)
        // This prevents the camera from tilting upward to look at the character
        ConfigureRigComposers();
        
        // Configure heading settings on each rig's orbital transposer
        ConfigureRigHeadings();
        
        if (debugLogging)
        {
            Debug.Log("[CameraManager] Camera fully initialized:");
            Debug.Log($"  Y Axis = {mainFreeLookCamera.m_YAxis.Value} (0.5 = middle rig)");
            Debug.Log($"  Orbits: Top({mainFreeLookCamera.m_Orbits[0].m_Height}h, {mainFreeLookCamera.m_Orbits[0].m_Radius}r) " +
                      $"Mid({mainFreeLookCamera.m_Orbits[1].m_Height}h, {mainFreeLookCamera.m_Orbits[1].m_Radius}r) " +
                      $"Bot({mainFreeLookCamera.m_Orbits[2].m_Height}h, {mainFreeLookCamera.m_Orbits[2].m_Radius}r)");
            Debug.Log($"  Heading: {mainFreeLookCamera.m_Heading.m_Definition}");
        }
    }
    
    /// <summary>
    /// Configure the Composer aim component on each rig.
    /// This sets WHERE on screen the character is framed.
    /// Without this, ScreenY defaults to 0.27 which makes the camera look UP.
    /// Pattern from WaitingSceneManager which correctly sets these values.
    /// </summary>
    private void ConfigureRigComposers()
    {
        for (int i = 0; i < 3; i++)
        {
            var rig = mainFreeLookCamera.GetRig(i);
            if (rig == null) continue;
            
            var composer = rig.GetCinemachineComponent<CinemachineComposer>();
            if (composer != null)
            {
                composer.m_ScreenX = 0.5f;        // Character centered horizontally
                composer.m_ScreenY = 0.4f;        // Character at 40% — standard third person
                composer.m_DeadZoneWidth = 0.1f;  // Small dead zone for responsive aiming
                composer.m_DeadZoneHeight = 0.1f;
                composer.m_SoftZoneWidth = 0.8f;  // Wide soft zone for smooth transitions
                composer.m_SoftZoneHeight = 0.8f;
                composer.m_LookaheadTime = 0f;    // No lookahead for combat (responsive)
                composer.m_LookaheadSmoothing = 0f;
                
                if (debugLogging)
                {
                    string rigName = i == 0 ? "Top" : (i == 1 ? "Middle" : "Bottom");
                    Debug.Log($"[CameraManager] {rigName} rig Composer: ScreenY={composer.m_ScreenY}, ScreenX={composer.m_ScreenX}");
                }
            }
        }
    }
    
    /// <summary>
    /// Configure heading settings on each rig's orbital transposer
    /// </summary>
    private void ConfigureRigHeadings()
    {
        for (int i = 0; i < 3; i++)
        {
            var rig = mainFreeLookCamera.GetRig(i);
            if (rig != null)
            {
                var transposer = rig.GetCinemachineComponent<CinemachineOrbitalTransposer>();
                if (transposer != null)
                {
                    // Enable recenter to target heading on each rig
                    transposer.m_RecenterToTargetHeading.m_enabled = true;
                    transposer.m_RecenterToTargetHeading.m_WaitTime = 0.3f;
                    transposer.m_RecenterToTargetHeading.m_RecenteringTime = 0.8f;
                }
            }
        }
    }
    
    /// <summary>
    /// Set target by GameObject (convenience method)
    /// </summary>
    public void SetTarget(GameObject target)
    {
        if (target == null) return;
        
        TeamMember member = target.GetComponent<TeamMember>();
        if (member != null)
        {
            SetTarget(member);
        }
        else
        {
            // Fallback: set camera directly with correct separation
            Transform followTransform = target.transform; // Root = orbit center
            Transform lookAtTransform = FindCharacterHead(target); // Head = aim point
            if (mainFreeLookCamera != null)
            {
                mainFreeLookCamera.Follow = followTransform;
                mainFreeLookCamera.LookAt = lookAtTransform;
            }
        }
    }
    
    /// <summary>
    /// Get the currently followed character
    /// </summary>
    public TeamMember GetCurrentTarget()
    {
        return currentTarget;
    }
    
    /// <summary>
    /// Find the character's head transform for camera targeting
    /// </summary>
    private Transform FindCharacterHead(GameObject character)
    {
        if (character == null) return null;
        
        // Try to find common head bone names
        string[] headNames = { "mixamorig:Head", "Head", "head", "Bip001 Head", "Bone_Head", "head_bone" };
        
        foreach (string headName in headNames)
        {
            Transform head = FindChildRecursive(character.transform, headName);
            if (head != null)
            {
                if (debugLogging)
                {
                    Debug.Log($"[CameraManager] Found head bone '{headName}' for {character.name}");
                }
                return head;
            }
        }
        
        // Try character-specific paths
        string characterName = character.name;
        try
        {
            Transform headTransform = null;
            if (characterName.Contains("Cheetah"))
                headTransform = character.transform.GetChild(9)?.GetChild(6)?.GetChild(1);
            else if (characterName.Contains("Rabbit") || characterName.Contains("Eagle") || 
                     characterName.Contains("Hyena") || characterName.Contains("HM") || 
                     characterName.Contains("Cat"))
                headTransform = character.transform.GetChild(7)?.GetChild(6)?.GetChild(1);
                
            if (headTransform != null)
            {
                if (debugLogging)
                {
                    Debug.Log($"[CameraManager] Found head via hardcoded path for {characterName}: {headTransform.name}");
                }
                return headTransform;
            }
        }
        catch { }
        
        // Final fallback - log warning
        Debug.LogWarning($"[CameraManager] Could not find head bone for {characterName}, using root transform. This will cause camera to show legs!");
        return character.transform;
    }
    
    private Transform FindChildRecursive(Transform parent, string name)
    {
        if (parent.name == name) return parent;
        foreach (Transform child in parent)
        {
            Transform result = FindChildRecursive(child, name);
            if (result != null) return result;
        }
        return null;
    }
}
