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

public enum UpgradeTypes
{
    ENEMY_HEALTH,
    ENEMY_DAMAGE,
    ENEMY_ARMOR,
    MANA_COST,
}

public enum FighterTypes
{
    ANTI_PLAYER,
    ENEMY,
}

public struct FighterStats
{
    public float healthMultiplier;
    public float damageMultiplier;
    public float armorMultiplier;
}

public class UpgradeData
{
    public Upgrade upgrade;
    public int count;

    // How much it costs to purchase another one of this upgrade
    public int GetNextUpgradeCost()
    {
        return StatsManager.instance.AdjustManaCost(GetUnadjustedNextUpgradeCost());
    }

    public float GetTotalEffectMultiplier()
    {
        return 1 + count * upgrade.effectMultiplier;
    }

    // You probably shouldn't call this outside this file.
    public int GetUnadjustedNextUpgradeCost()
    {
        return (int)(upgrade.baseCost * (1 + count * upgrade.costMultiplier));
    }
}

public class StatsManager : MonoBehaviour
{

    public static StatsManager instance { get; private set; }

    [SerializeField] int currentMana;
    [SerializeField, Tooltip("When the AntiPlayer receives damage, how much of that damage is converted to Mana")]
    float antiPlayerDamageManaMultiplier = 100f;
    [SerializeField, Tooltip("When the AntiPlayer dies, how much mana to give")]
    int antiPlayerDeathMana = 1000;
    [SerializeField] List<Upgrade> availableUpgrades;

    // The currently active statistics for each fighter type
    public Dictionary<FighterTypes, FighterStats> fighterStats = new Dictionary<FighterTypes, FighterStats>();

    // Information about currently available and purchased upgrades. Should only be read, not modified.
    public Dictionary<UpgradeTypes, UpgradeData> upgrades = new Dictionary<UpgradeTypes, UpgradeData>();

    public int GetMana()
    {
        return currentMana;
    }

    // Adjusts the given Mana cost based on the amount of Mana cost upgrades currently bought.
    public int AdjustManaCost(int cost)
    {
        return (int)(cost * upgrades[UpgradeTypes.MANA_COST].GetTotalEffectMultiplier());
    }

    public int GetNextUpgradeCost(UpgradeTypes type)
    {
        return upgrades[type].GetNextUpgradeCost();
    }

    // Buys an upgrade, spending the required amount of Mana in the process. Returns false if not enough Mana is available.
    public bool BuyUpgrade(UpgradeTypes type)
    {
        if (!ModifyMana(-GetNextUpgradeCost(type)))
        {
            return false;
        }
        upgrades[type].count++;
        RecalculateStatMultipliers();
        return true;
    }

    // Add (positive value) or spend (negative value) Mana. Returns false if not enough Mana is available to spend.
    public bool ModifyMana(int delta)
    {
        if (!CanModifyMana(delta))
        {
            return false;
        }
        currentMana += delta;
        return true;
    }

    // If `delta` is negative, returns this amount of Mana can be spent. If `delta` is positive, this will always return `true`.
    public bool CanModifyMana(int delta)
    {
        return currentMana + delta >= 0;
    }

    // Adds Mana based on the amount of damage the AntiPlayer has taken
    public void AddDamageBasedMana(int damage, bool death = false)
    {
        if (death)
        {
            ModifyMana(antiPlayerDeathMana);
        }
        else
        {
            ModifyMana((int)(damage * antiPlayerDamageManaMultiplier));
        }
    }

    void RecalculateStatMultipliers()
    {
        var enemyStats = fighterStats[FighterTypes.ENEMY];
        enemyStats.healthMultiplier = upgrades[UpgradeTypes.ENEMY_HEALTH].GetTotalEffectMultiplier();
        enemyStats.damageMultiplier = upgrades[UpgradeTypes.ENEMY_DAMAGE].GetTotalEffectMultiplier();
        enemyStats.armorMultiplier = upgrades[UpgradeTypes.ENEMY_ARMOR].GetTotalEffectMultiplier();
        fighterStats[FighterTypes.ENEMY] = enemyStats;
    }

    public float GetTotalEffectMultiplier(UpgradeTypes type)
    {
        return upgrades[type].GetTotalEffectMultiplier();
    }

    void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else if (instance != this)
        {
            Destroy(gameObject);
        }
        DontDestroyOnLoad(gameObject);

        foreach (Upgrade upgrade in availableUpgrades)
        {
            UpgradeData upgradeData = new UpgradeData();
            upgradeData.upgrade = upgrade;
            upgradeData.count = 0;
            upgrades.Add(upgrade.type, upgradeData);
        }

        fighterStats.Add(FighterTypes.ANTI_PLAYER, new FighterStats()
        {
            healthMultiplier = 1f,
            damageMultiplier = 1f,
            armorMultiplier = 1f,
        });
        fighterStats.Add(FighterTypes.ENEMY, new FighterStats()
        {
            healthMultiplier = 1f,
            damageMultiplier = 1f,
            armorMultiplier = 1f,
        });
    }
}