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

/// <summary>
/// GAME 1303 Tier 8 — MythoClash's loop: a monster appears, you choose FIGHT or SKIP,
/// and skipping makes the next one likelier to be nasty. Fozaan's core design.
///
/// THE RULES FROM THE PITCH
///  - Beat a monster, earn Drachma (ExperienceWallet).
///  - You may skip, but each skip raises the chance of drawing a higher-tier monster.
///  - Skips are limited (Fozaan suggested 3) so you can't skip your way past the game.
///  - Monsters come from a roster; each world is one pantheon.
///
/// SETUP
///  1. Empty GameObject "EncounterManager" + this script.
///  2. Roster: drag in MonsterEntry assets. Filter By Pantheon restricts this world to
///     one mythology (leave blank for all).
///  3. Spawn Point: where the monster appears. The prefab needs Health + a Hurtbox
///     (Tier 2) so the player can actually fight it; wire its Health.OnDied to
///     EncounterManager.OnMonsterDefeated via the spawned instance (this script does it
///     automatically if the prefab has a Health component).
///  4. Fight / Skip buttons: wire them to ChooseFight() and ChooseSkip().
///  5. Difficulty: Base Tier is what you'd normally draw; each unused skip adds
///     Tier Per Skip to the roll. Max Skips is the hard limit.
///  6. Encounters Per World: how many fights before OnWorldCleared (Fozaan said ~12).
///
/// The interesting design question to put to Fozaan: skipping should feel like a real
/// temptation, which means the tier bump has to be visible. Show the player the risk
/// ("Threat rising") rather than hiding it — a gamble you can't see isn't a decision.
/// </summary>
public class EncounterManager : MonoBehaviour
{
    [Header("Roster")]
    public List<MonsterEntry> roster = new List<MonsterEntry>();
    [Tooltip("Only draw monsters from this pantheon. Blank = any.")]
    public string filterByPantheon = "";
    public bool avoidImmediateRepeats = true;

    [Header("Where")]
    public Transform spawnPoint;
    public Transform playerStart;

    [Header("Difficulty")]
    [Range(1, 5)] public int baseTier = 1;
    [Tooltip("Each skip taken raises the drawn tier by this much.")]
    public float tierPerSkip = 0.8f;
    public int maxSkips = 3;
    [Tooltip("Tier also creeps up as you clear encounters.")]
    public float tierPerEncounter = 0.15f;

    [Header("World")]
    public int encountersPerWorld = 12;
    public bool autoStart = true;
    public float delayBetweenEncounters = 1.5f;

    [Header("Events")]
    public UnityEvent<MonsterEntry> OnMonsterPresented;
    public UnityEvent<MonsterEntry> OnFightStarted;
    public UnityEvent<MonsterEntry> OnMonsterDefeated;
    public UnityEvent<int> OnSkipsRemainingChanged;
    public UnityEvent<int> OnThreatLevelChanged;      // the tier you'd draw next
    public UnityEvent<int, int> OnProgressChanged;    // (cleared, total)
    public UnityEvent OnWorldCleared;
    public UnityEvent OnOutOfSkips;

    public MonsterEntry CurrentMonster { get; private set; }
    public int SkipsUsed { get; private set; }
    public int Cleared { get; private set; }
    public int SkipsRemaining => Mathf.Max(0, maxSkips - SkipsUsed);
    public int ThreatTier => Mathf.Clamp(
        Mathf.RoundToInt(baseTier + SkipsUsed * tierPerSkip + Cleared * tierPerEncounter), 1, 5);

    private GameObject currentInstance;
    private MonsterEntry lastPresented;
    private bool awaitingChoice;

    void Start()
    {
        OnSkipsRemainingChanged?.Invoke(SkipsRemaining);
        OnProgressChanged?.Invoke(Cleared, encountersPerWorld);
        OnThreatLevelChanged?.Invoke(ThreatTier);
        if (autoStart) PresentNext();
    }

    /// <summary>Draw and show the next monster (without spawning it yet).</summary>
    public void PresentNext()
    {
        if (Cleared >= encountersPerWorld) { OnWorldCleared?.Invoke(); return; }

        CurrentMonster = Draw();
        if (CurrentMonster == null)
        {
            Debug.LogWarning("EncounterManager: the roster is empty (or the pantheon filter matches nothing).");
            return;
        }

        lastPresented = CurrentMonster;
        awaitingChoice = true;
        OnMonsterPresented?.Invoke(CurrentMonster);
        OnThreatLevelChanged?.Invoke(ThreatTier);
    }

    MonsterEntry Draw()
    {
        var pool = new List<MonsterEntry>();
        foreach (var m in roster)
        {
            if (m == null) continue;
            if (!string.IsNullOrEmpty(filterByPantheon) && m.pantheon != filterByPantheon) continue;
            if (avoidImmediateRepeats && m == lastPresented && roster.Count > 1) continue;
            pool.Add(m);
        }
        if (pool.Count == 0) return null;

        int target = ThreatTier;

        // Weight each monster by how close its tier is to the target — so a high threat
        // makes tough monsters likely without making weak ones impossible.
        float total = 0f;
        var weights = new List<float>();
        foreach (var m in pool)
        {
            float w = 1f / (1f + Mathf.Abs(m.difficultyTier - target));
            weights.Add(w);
            total += w;
        }

        float r = Random.value * total;
        for (int i = 0; i < pool.Count; i++)
        {
            r -= weights[i];
            if (r <= 0f) return pool[i];
        }
        return pool[pool.Count - 1];
    }

    /// <summary>Wire the FIGHT button here.</summary>
    public void ChooseFight()
    {
        if (!awaitingChoice || CurrentMonster == null) return;
        awaitingChoice = false;

        if (CurrentMonster.monsterPrefab != null)
        {
            Vector3 where = spawnPoint != null ? spawnPoint.position : transform.position;
            currentInstance = Instantiate(CurrentMonster.monsterPrefab, where, Quaternion.identity);

            var hp = currentInstance.GetComponent<Health>();
            if (hp != null)
            {
                hp.maxHealth = CurrentMonster.maxHealth;
                hp.ResetHealth();
                hp.OnDied.AddListener(HandleMonsterDied);
            }
            else Debug.LogWarning($"Monster prefab '{CurrentMonster.monsterName}' has no Health component — it can't be defeated.");
        }

        if (CurrentMonster.roarSound != null) AudioManager.Instance?.PlaySFX(CurrentMonster.roarSound);
        OnFightStarted?.Invoke(CurrentMonster);
    }

    /// <summary>Wire the SKIP button here.</summary>
    public void ChooseSkip()
    {
        if (!awaitingChoice) return;

        if (SkipsRemaining <= 0)
        {
            OnOutOfSkips?.Invoke();
            return;
        }

        SkipsUsed++;
        awaitingChoice = false;
        OnSkipsRemainingChanged?.Invoke(SkipsRemaining);
        ChoiceFlagManager.Instance?.AddCount("monsters_skipped", 1);

        StartCoroutine(NextAfterDelay());
    }

    void HandleMonsterDied()
    {
        var defeated = CurrentMonster;
        Cleared++;

        ExperienceWallet.Instance?.Add(defeated.experienceReward);
        ChoiceFlagManager.Instance?.AddCount("monsters_defeated", 1);
        ChoiceFlagManager.Instance?.SetFlag("defeated_" + defeated.name);

        OnMonsterDefeated?.Invoke(defeated);
        OnProgressChanged?.Invoke(Cleared, encountersPerWorld);

        StartCoroutine(NextAfterDelay());
    }

    IEnumerator NextAfterDelay()
    {
        yield return new WaitForSeconds(delayBetweenEncounters);

        if (currentInstance != null) Destroy(currentInstance);

        // Put the player back on their mark between fights
        if (playerStart != null)
        {
            var p = GameObject.FindGameObjectWithTag("Player");
            if (p != null)
            {
                p.transform.position = playerStart.position;
                p.GetComponent<PlayerController2D>()?.ResetMotion();
                p.GetComponent<Health>()?.ResetHealth();
            }
        }

        PresentNext();
    }

    /// <summary>Start a new world with a fresh roster and skip allowance.</summary>
    public void StartWorld(string pantheon)
    {
        filterByPantheon = pantheon;
        Cleared = 0;
        SkipsUsed = 0;
        lastPresented = null;
        OnSkipsRemainingChanged?.Invoke(SkipsRemaining);
        OnProgressChanged?.Invoke(Cleared, encountersPerWorld);
        PresentNext();
    }
}