using System.Collections;
using UnityEngine;

public class Gun : MonoBehaviour
{
    [Header("Stats")]
    public string weaponName = "Pistol";
    public bool automatic;
    public float damage = 20f;
    public float range = 90f;
    public float fireRate = 3f;
    public float spread = 0.008f;
    public int projectilesPerShot = 1;
    public int magazineSize = 12;
    public int reserveAmmo = 48;
    public float reloadTime = 1.1f;
    public float recoilPitch = 1.6f;
    public float recoilYaw = 0.45f;
    public Color tracerColor = new Color(1f, 0.85f, 0.3f);

    [Header("References")]
    public Transform muzzlePoint;
    public ParticleSystem muzzleFlash;
    public AudioSource shootAudioSource;
    public AudioSource reloadAudioSource;
    public AudioClip shootClip;
    public AudioClip reloadClip;

    [Header("Audio")]
    public Vector2 shootPitchRange = new Vector2(0.98f, 1.02f);
    public Vector2 reloadPitchRange = new Vector2(0.98f, 1.02f);
    public float shootVolume = 1f;
    public float reloadVolume = 1f;

    public int AmmoInMagazine { get; private set; }
    public int ReserveAmmo => reserveAmmo;
    public bool IsReloading { get; private set; }

    private static Material _sharedTracerMaterial;

    private Camera _firingCamera;
    private FPSController _controller;
    private HUDController _hud;
    private WeaponSwitcher _weaponSwitcher;
    private float _nextFireTime;
    private Coroutine _reloadRoutine;
    private bool _initialized;

    public void Initialize(Camera firingCamera, FPSController controller, HUDController hud, WeaponSwitcher weaponSwitcher)
    {
        _firingCamera = firingCamera;
        _controller = controller;
        _hud = hud;
        _weaponSwitcher = weaponSwitcher;
        AmmoInMagazine = magazineSize;
        _initialized = true;
    }

    public void BindOwnership(HUDController hud, WeaponSwitcher weaponSwitcher)
    {
        _hud = hud;
        _weaponSwitcher = weaponSwitcher;
        UpdateHud();
    }

    private void OnEnable()
    {
        EnsureInitialized();
        UpdateHud();
    }

    private void OnDisable()
    {
        CancelReload();
        muzzleFlash?.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
    }

    private void Update()
    {
        EnsureInitialized();
        if (_firingCamera == null)
        {
            return;
        }

        if (Input.GetKeyDown(KeyCode.R))
        {
            TryReload();
        }

        bool firePressed = automatic ? Input.GetButton("Fire1") : Input.GetButtonDown("Fire1");
        if (firePressed)
        {
            TryFire();
        }
    }

    public void SetEquipCooldown(float cooldown)
    {
        _nextFireTime = Mathf.Max(_nextFireTime, Time.time + cooldown);
    }

    public bool AddReserveAmmo(int amount)
    {
        if (amount <= 0)
        {
            return false;
        }

        reserveAmmo += amount;
        UpdateHud();
        return true;
    }

    public void CancelReload()
    {
        if (_reloadRoutine != null)
        {
            StopCoroutine(_reloadRoutine);
            _reloadRoutine = null;
        }

        IsReloading = false;
        if (reloadAudioSource != null)
        {
            reloadAudioSource.Stop();
        }
        UpdateHud();
    }

    private void TryFire()
    {
        if (IsReloading || Time.time < _nextFireTime)
        {
            return;
        }

        if (AmmoInMagazine <= 0)
        {
            TryReload();
            return;
        }

        AmmoInMagazine--;
        _nextFireTime = Time.time + (1f / fireRate);
        _controller?.ApplyRecoil(recoilPitch, recoilYaw);
        PlayShootEffects();

        Vector3 tracerStart = muzzlePoint != null ? muzzlePoint.position : _firingCamera.transform.position;
        Vector3 lastEndPoint = tracerStart + _firingCamera.transform.forward * range;

        for (int i = 0; i < Mathf.Max(1, projectilesPerShot); i++)
        {
            Vector3 viewportPoint = new Vector3(
                0.5f + Random.Range(-spread, spread),
                0.5f + Random.Range(-spread, spread),
                0f);

            Ray ray = _firingCamera.ViewportPointToRay(viewportPoint);
            Vector3 endPoint = ray.origin + ray.direction * range;

            if (Physics.Raycast(ray, out RaycastHit hit, range, ~0, QueryTriggerInteraction.Ignore))
            {
                endPoint = hit.point;
                IDamageable damageable = hit.collider.GetComponentInParent<IDamageable>();
                damageable?.TakeDamage(damage, hit.point, hit.normal);
            }

            lastEndPoint = endPoint;

            if (projectilesPerShot == 1 || i < 2 || i == projectilesPerShot - 1)
            {
                ShowTracer(tracerStart, endPoint, tracerColor);
            }
        }

        if (projectilesPerShot > 1)
        {
            ShowTracer(tracerStart, lastEndPoint, tracerColor);
        }

        UpdateHud();
    }

    private void TryReload()
    {
        if (IsReloading || AmmoInMagazine >= magazineSize || reserveAmmo <= 0)
        {
            return;
        }

        _reloadRoutine = StartCoroutine(ReloadRoutine());
    }

    private IEnumerator ReloadRoutine()
    {
        IsReloading = true;
        UpdateHud();
        PlayReloadSound();

        yield return new WaitForSeconds(reloadTime);

        int ammoNeeded = magazineSize - AmmoInMagazine;
        int ammoToLoad = Mathf.Min(ammoNeeded, reserveAmmo);
        AmmoInMagazine += ammoToLoad;
        reserveAmmo -= ammoToLoad;

        IsReloading = false;
        _reloadRoutine = null;
        UpdateHud();
    }

    private void UpdateHud()
    {
        if (_hud == null || _weaponSwitcher == null || _weaponSwitcher.CurrentGun != this)
        {
            return;
        }

        _hud.SetWeapon(weaponName);
        _hud.SetAmmo(AmmoInMagazine, reserveAmmo);
        _hud.SetCenterMessage(IsReloading ? "Reloading..." : string.Empty);
    }

    private void EnsureInitialized()
    {
        if (_firingCamera == null)
        {
            _firingCamera = GetComponentInParent<Camera>();
            if (_firingCamera == null)
            {
                _firingCamera = Camera.main;
            }
        }

        if (_controller == null)
        {
            _controller = GetComponentInParent<FPSController>();
        }

        if (_weaponSwitcher == null)
        {
            _weaponSwitcher = GetComponentInParent<WeaponSwitcher>();
        }

        if (_hud == null)
        {
            _hud = FindObjectOfType<HUDController>();
        }

        if (muzzlePoint == null)
        {
            Transform foundMuzzle = transform.Find("Muzzle");
            if (foundMuzzle != null)
            {
                muzzlePoint = foundMuzzle;
            }
        }

        if (muzzleFlash == null)
        {
            Transform flashTransform = muzzlePoint != null ? muzzlePoint.Find("MuzzleFlash") : transform.Find("Muzzle/MuzzleFlash");
            if (flashTransform != null)
            {
                muzzleFlash = flashTransform.GetComponent<ParticleSystem>();
            }
            else
            {
                muzzleFlash = GetComponentInChildren<ParticleSystem>(true);
            }
        }

        if (shootAudioSource == null)
        {
            Transform shootTransform = transform.Find("ShootAudio");
            if (shootTransform != null)
            {
                shootAudioSource = shootTransform.GetComponent<AudioSource>();
            }
        }

        if (reloadAudioSource == null)
        {
            Transform reloadTransform = transform.Find("ReloadAudio");
            if (reloadTransform != null)
            {
                reloadAudioSource = reloadTransform.GetComponent<AudioSource>();
            }
        }

        if (shootClip == null)
        {
            shootClip = Resources.Load<AudioClip>($"Audio/{weaponName.ToLowerInvariant()}_fire");
        }

        if (reloadClip == null)
        {
            reloadClip = Resources.Load<AudioClip>($"Audio/{weaponName.ToLowerInvariant()}_reload");
        }

        if (AmmoInMagazine <= 0)
        {
            AmmoInMagazine = magazineSize;
        }

        _initialized = _firingCamera != null;
    }

    private void PlayShootEffects()
    {
        if (muzzleFlash != null)
        {
            muzzleFlash.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
            muzzleFlash.Play();
        }

        PlayClip(shootAudioSource, shootClip, shootPitchRange, shootVolume, false);
    }

    private void PlayReloadSound()
    {
        PlayClip(reloadAudioSource, reloadClip, reloadPitchRange, reloadVolume, true);
    }

    private static void PlayClip(AudioSource source, AudioClip clip, Vector2 pitchRange, float volume, bool replaceClip)
    {
        if (source == null || clip == null)
        {
            return;
        }

        source.pitch = Random.Range(pitchRange.x, pitchRange.y);
        source.volume = volume;

        if (replaceClip)
        {
            source.Stop();
            source.clip = clip;
            source.Play();
            return;
        }

        source.PlayOneShot(clip, volume);
    }

    private void ShowTracer(Vector3 start, Vector3 end, Color color)
    {
        GameObject tracer = new GameObject($"{weaponName}_Tracer");
        LineRenderer renderer = tracer.AddComponent<LineRenderer>();
        renderer.material = GetTracerMaterial();
        renderer.positionCount = 2;
        renderer.SetPosition(0, start);
        renderer.SetPosition(1, end);
        renderer.startWidth = 0.032f;
        renderer.endWidth = 0.012f;
        renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
        renderer.receiveShadows = false;
        renderer.startColor = color;
        renderer.endColor = new Color(color.r, color.g, color.b, 0.1f);

        Destroy(tracer, 0.06f);
    }

    private static Material GetTracerMaterial()
    {
        if (_sharedTracerMaterial == null)
        {
            _sharedTracerMaterial = new Material(Shader.Find("Sprites/Default"));
        }

        return _sharedTracerMaterial;
    }
}
