using System;
using System.Collections;
using UnityEngine;
using Random = UnityEngine.Random;

public enum BattleResult {
    Win,
    Lose,
    Escape,
    Surrender
}

public sealed class BattleController : MonoBehaviour
{
    [SerializeField]
    private BattleUI battleUI = null;
    [SerializeField]
    private OrbitalCameraControl orbitalCamera = null;

    private ShipView playerShipView = null;
    private ShipView enemyShipView = null;
    private StarShip playerShip = null;
    private StarShip enemyShip = null;
    private Action<BattleResult> resultCallback = null;

    public void StartBattle(ShipView player, ShipView enemy, Action<BattleResult> callback) {
        playerShipView = player;
        enemyShipView = enemy;
        playerShip = player.Ship;
        enemyShip = enemy.Ship;
        playerShip.RestoreShield();
        enemyShip.RestoreShield();
        resultCallback = callback;
        battleUI.Show(playerShip, enemyShip);
        orbitalCamera.LockControl(true);
        orbitalCamera.LookAt(enemy.transform.position, 0.6f, 10, -15);
    }

    public void Fire()
    {
        LockControls(0.6f);
        var damage = TryFire(playerShip, enemyShip, 0.5f);
        ShowHit(playerShipView, enemyShipView, damage);
        battleUI.UpdateEnemy(enemyShip);
        if (IsDeath(enemyShip))
        {
            ShowDelayed(() => enemyShipView.ShowTextEffect("DEATH!", 1f), 0.6f);
            EndBattle(BattleResult.Win);
        }
        else
        {
            ShowDelayed(() => {
                damage = TryFire(enemyShip, playerShip, 0.5f);
                ShowHit(enemyShipView, playerShipView, damage);
                battleUI.UpdatePlayer(playerShip);
                if (IsDeath(playerShip))
                {
                    ShowDelayed(() => playerShipView.ShowTextEffect("DEATH!", 1f), 0.6f);
                    EndBattle(BattleResult.Lose);
                }
            }, 0.6f);
        }
    }

    private int TryFire(StarShip attacker, StarShip defender, float chance)
    {
        if (Random.Range(0f, 1f) > chance)
        {
            // hit
            defender.Hit(attacker.Damage);
            return attacker.Damage;
        }
        // miss
        return 0;
    }

    public void TryFlee()
    {
        const float fleeChance = 0.33f;
        if (Random.Range(0f, 1f) < fleeChance)
        {
            playerShipView.ShowTextEffect("FLEE!", 1f);
            EndBattle(BattleResult.Escape);
        }
        else
        {
            LockControls(0.65f);
            playerShipView.ShowTextEffect("FAIL TO FLEE!", 1f);
            ShowDelayed(() => {
                var damage = TryFire(enemyShip, playerShip, 0.5f);
                ShowHit(enemyShipView, playerShipView, damage);
                battleUI.UpdatePlayer(playerShip);
                if (IsDeath(playerShip))
                {
                    ShowDelayed(() => playerShipView.ShowTextEffect("DEATH!", 1f), 0.6f);
                    EndBattle(BattleResult.Lose);
                }
            }, 0.6f);
        }
    }

    private void LockControls(float time)
    {
        battleUI.LockControl(true);
        ShowDelayed(() => battleUI.LockControl(false), time);
    }

    private void ShowHit(ShipView attacker, ShipView defence, int damage)
    {
        if (damage == 0) {
            defence.ShowTextEffect("Miss!", 0.8f);
            attacker.Miss(defence);
            return;
        }
        defence.ShowTextEffect(damage.ToString(), 0.8f);
        attacker.Fire(defence);
    }

    private bool IsDeath(StarShip ship) => ship.Armor <= 0;

    public void ShowDelayed(Action action, float delay = 0) {
        StartCoroutine(ShowDelayedCoroutine(action, delay));
    }

    private IEnumerator ShowDelayedCoroutine(Action action, float delay)
    {
        while (delay > 0)
        {
            delay -= Time.deltaTime;
            yield return null;
        }
        action?.Invoke();
    }

    public void Surrender()
    {
        EndBattle(BattleResult.Surrender);
    }

    private void EndBattle(BattleResult result)
    {
        Debug.Log("End battle " + result);
        orbitalCamera.LockControl(false);
        battleUI.Hide();
        var callback = resultCallback;
        resultCallback = null;
        callback?.Invoke(result);
    }

    private void Awake()
    {
        battleUI.Hide();
        battleUI.fireClick += Fire;
        battleUI.surrenderClick += Surrender;
        battleUI.escapeClick += TryFlee;
    }
#if UNITY_EDITOR
    [ContextMenu("test my hit")]
    private void TestMyHit()
    {
        Timer.Add(0.5f, () => {
            ShowHit(playerShipView, enemyShipView, 5);
        });
    }
    [ContextMenu("test my miss")]
    private void TestMyMiss()
    {
        Timer.Add(0.5f, () => {
            ShowHit(playerShipView, enemyShipView, 0);
        });
    }
    [ContextMenu("test enemy hit")]
    private void TestEnemyHit()
    {
        Timer.Add(0.5f, () => {
            ShowHit(enemyShipView, playerShipView, 5);
        });
    }
    [ContextMenu("test enemy miss")]
    private void TestEnemyMiss()
    {
        Timer.Add(0.5f, () => {
            ShowHit(enemyShipView, playerShipView, 0);
        });
    }
#endif
}
