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

/// <summary>
/// GAME 1303 Tier 8 — The cauldron. Compares what the witch gathered against the order
/// and grades the result. Commission Witch.
///
/// SETUP
///  1. Cauldron object + Collider2D (trigger) + Tier 6 Interactable + this script.
///     Wire the Interactable's OnInteract -> RecipeChecker.Brew.
///  2. Current Recipe: the active order. Set it from an order-board Interactable
///     (OnInteract -> RecipeChecker.SetRecipe with the Recipe asset dragged in).
///  3. Ingredients come from Tier 1's CollectionTracker — the witch picks up Collectibles
///     out in the world and they count automatically.
///  4. Consume On Brew spends them (so a failed brew costs you the ingredients — harsh but
///     it makes gathering matter). Turn it off while playtesting.
///  5. Results feed the ending: each brew adds to counters "brews_perfect", "brews_passable"
///     and "brews_ruined", which Tier 6's EndingSelector reads with Min Counter Value.
///     That's how "different endings based on how many and which ingredients you collect"
///     actually resolves.
///
/// GRADES
///   Perfect  – every required ingredient, no forbidden ones, no excess beyond the allowance
///   Passable – required ingredients present, but too many extras
///   Ruined   – a forbidden ingredient went in
///   Failed   – something required is missing
/// </summary>
public class RecipeChecker : MonoBehaviour
{
    public enum Result { Perfect, Passable, Ruined, Failed }

    [Header("Order")]
    public Recipe currentRecipe;
    public bool consumeOnBrew = true;

    [Header("Feedback")]
    public string brewSfx = "brew";
    public string successSfx = "success";
    public string failSfx = "fail";
    public GameObject successEffect;
    public GameObject failEffect;

    [Header("Events")]
    public UnityEvent<Recipe> OnRecipeSet;
    public UnityEvent<Result> OnBrewResult;
    public UnityEvent<string> OnFeedback;        // a human-readable line for the UI
    public UnityEvent OnPerfect;
    public UnityEvent OnRuined;
    public UnityEvent<int, int> OnIngredientProgress;   // (have, needed) totals

    public Result LastResult { get; private set; }

    /// <summary>Wire an order-board Interactable here, with the Recipe asset in the slot.</summary>
    public void SetRecipe(Recipe recipe)
    {
        currentRecipe = recipe;
        OnRecipeSet?.Invoke(recipe);
        ReportProgress();
    }

    void Update()
    {
        // Cheap enough at this scale, and it keeps a "3 / 5 gathered" readout live.
        if (currentRecipe != null) ReportProgress();
    }

    void ReportProgress()
    {
        if (CollectionTracker.Instance == null || currentRecipe == null) return;
        int have = 0, need = 0;
        foreach (var ing in currentRecipe.required)
        {
            if (ing.item == null) continue;
            need += ing.amount;
            have += Mathf.Min(ing.amount, CollectionTracker.Instance.GetCount(ing.item));
        }
        OnIngredientProgress?.Invoke(have, need);
    }

    /// <summary>Wire the cauldron's Interactable OnInteract here.</summary>
    public void Brew()
    {
        if (currentRecipe == null) { OnFeedback?.Invoke("No order to fill."); return; }
        if (CollectionTracker.Instance == null) { Debug.LogWarning("RecipeChecker: no CollectionTracker in the scene."); return; }

        if (!string.IsNullOrEmpty(brewSfx)) AudioManager.Instance?.PlaySFX(brewSfx);

        var tracker = CollectionTracker.Instance;

        // 1. Forbidden ingredients ruin it outright
        foreach (var bad in currentRecipe.forbidden)
        {
            if (bad == null) continue;
            if (tracker.GetCount(bad) > 0)
            {
                Finish(Result.Ruined, $"The {bad.displayName} turned it black. Ruined.");
                return;
            }
        }

        // 2. Everything required must be present
        var missing = new List<string>();
        foreach (var ing in currentRecipe.required)
        {
            if (ing.item == null) continue;
            if (tracker.GetCount(ing.item) < ing.amount)
                missing.Add($"{ing.amount - tracker.GetCount(ing.item)}x {ing.item.displayName}");
        }
        if (missing.Count > 0)
        {
            Finish(Result.Failed, "Still missing: " + string.Join(", ", missing));
            return;
        }

        // 3. Count anything carried that the recipe doesn't call for
        int extras = 0;
        foreach (var kv in tracker.GetAllCounts())
        {
            int needed = 0;
            foreach (var ing in currentRecipe.required)
                if (ing.item == kv.Key) needed = ing.amount;
            extras += Mathf.Max(0, kv.Value - needed);
        }

        if (consumeOnBrew)
            foreach (var ing in currentRecipe.required)
                if (ing.item != null) tracker.Spend(ing.item, ing.amount);

        if (extras > currentRecipe.extrasAllowed)
            Finish(Result.Passable, $"It works, but it's cloudy — {extras} stray ingredients.");
        else
            Finish(Result.Perfect, $"{currentRecipe.potionName}, exactly as ordered.");
    }

    void Finish(Result result, string message)
    {
        LastResult = result;
        var flags = ChoiceFlagManager.Instance;

        switch (result)
        {
            case Result.Perfect:
                flags?.AddCount("brews_perfect", 1);
                if (!string.IsNullOrEmpty(currentRecipe.setFlagOnSuccess)) flags?.SetFlag(currentRecipe.setFlagOnSuccess);
                if (successEffect) Instantiate(successEffect, transform.position, Quaternion.identity);
                if (!string.IsNullOrEmpty(successSfx)) AudioManager.Instance?.PlaySFX(successSfx);
                OnPerfect?.Invoke();
                break;

            case Result.Passable:
                flags?.AddCount("brews_passable", 1);
                if (!string.IsNullOrEmpty(currentRecipe.setFlagOnSuccess)) flags?.SetFlag(currentRecipe.setFlagOnSuccess);
                if (!string.IsNullOrEmpty(successSfx)) AudioManager.Instance?.PlaySFX(successSfx);
                break;

            case Result.Ruined:
                flags?.AddCount("brews_ruined", 1);
                if (!string.IsNullOrEmpty(currentRecipe.setFlagOnFailure)) flags?.SetFlag(currentRecipe.setFlagOnFailure);
                if (failEffect) Instantiate(failEffect, transform.position, Quaternion.identity);
                if (!string.IsNullOrEmpty(failSfx)) AudioManager.Instance?.PlaySFX(failSfx);
                OnRuined?.Invoke();
                break;

            case Result.Failed:
                if (!string.IsNullOrEmpty(failSfx)) AudioManager.Instance?.PlaySFX(failSfx);
                break;
        }

        OnFeedback?.Invoke(message);
        OnBrewResult?.Invoke(result);
    }
}