using UnityEngine;

/// <summary>
/// GAME 1303 Tier 4 — Saves which levels are cleared, best times, and best collectible
/// counts. Uses PlayerPrefs, so it works with zero setup and survives quitting the game.
///
/// There is no component to add — it's a static helper any script can call:
///     LevelProgress.MarkCleared("sector1_level1");
///     LevelProgress.IsCleared(id);
///     LevelProgress.RecordTime(id, 42.3f);       // keeps the best
///     LevelProgress.GetBestTime(id);             // -1 if never finished
///     LevelProgress.IsUnlocked(levelData, levelSet);
///     LevelProgress.ResetAll();                  // a "clear save" button
///
/// Tier 7's SaveSystem can replace this with a JSON file later; keep the same method
/// names and nothing else has to change.
///
/// NOTE FOR CLASS: PlayerPrefs lives in the registry / a plist per project name. Two
/// students sharing a machine share it, and it does NOT reset when you rebuild — that's
/// usually the answer to "why is level 3 still unlocked?"
/// </summary>
public static class LevelProgress
{
    const string ClearedKey = "cleared_";
    const string TimeKey    = "besttime_";
    const string CollectKey = "bestitems_";
    const string UnlockKey  = "unlocked_";

    // ---- Cleared ----
    public static void MarkCleared(string levelId)
    {
        if (string.IsNullOrEmpty(levelId)) return;
        PlayerPrefs.SetInt(ClearedKey + levelId, 1);
        PlayerPrefs.Save();
    }

    public static bool IsCleared(string levelId) =>
        !string.IsNullOrEmpty(levelId) && PlayerPrefs.GetInt(ClearedKey + levelId, 0) == 1;

    // ---- Best time (seconds; lower is better) ----
    public static void RecordTime(string levelId, float seconds)
    {
        if (string.IsNullOrEmpty(levelId) || seconds <= 0f) return;
        float best = GetBestTime(levelId);
        if (best < 0f || seconds < best)
        {
            PlayerPrefs.SetFloat(TimeKey + levelId, seconds);
            PlayerPrefs.Save();
        }
    }

    /// <summary>Best time in seconds, or -1 if there isn't one.</summary>
    public static float GetBestTime(string levelId) =>
        string.IsNullOrEmpty(levelId) ? -1f : PlayerPrefs.GetFloat(TimeKey + levelId, -1f);

    public static bool IsNewRecord(string levelId, float seconds)
    {
        float best = GetBestTime(levelId);
        return best < 0f || seconds < best;
    }

    // ---- Best collectibles ----
    public static void RecordCollectibles(string levelId, int count)
    {
        if (string.IsNullOrEmpty(levelId)) return;
        if (count > GetBestCollectibles(levelId))
        {
            PlayerPrefs.SetInt(CollectKey + levelId, count);
            PlayerPrefs.Save();
        }
    }

    public static int GetBestCollectibles(string levelId) =>
        string.IsNullOrEmpty(levelId) ? 0 : PlayerPrefs.GetInt(CollectKey + levelId, 0);

    // ---- Purchased unlocks (spent currency) ----
    public static void MarkUnlocked(string levelId)
    {
        PlayerPrefs.SetInt(UnlockKey + levelId, 1);
        PlayerPrefs.Save();
    }

    public static bool WasPurchased(string levelId) => PlayerPrefs.GetInt(UnlockKey + levelId, 0) == 1;

    // ---- The unlock rule ----
    /// <summary>
    /// A level is playable if: it's marked unlocked from start, OR it was purchased before,
    /// OR every level in requiresCleared is cleared (when the list is empty: the previous
    /// level in the set is cleared), AND any unlock cost has been paid.
    /// </summary>
    public static bool IsUnlocked(LevelData level, LevelSet set = null)
    {
        if (level == null) return false;
        if (level.unlockedFromStart) return true;
        if (WasPurchased(level.levelId)) return true;

        // Explicit prerequisites
        if (level.requiresCleared != null && level.requiresCleared.Length > 0)
        {
            foreach (var id in level.requiresCleared)
                if (!IsCleared(id)) return false;
            return level.unlockCost <= 0;   // still needs paying if it costs something
        }

        // Default: the previous level in the set
        if (set != null)
        {
            int i = set.levels.IndexOf(level);
            if (i <= 0) return level.unlockCost <= 0;
            var prev = set.levels[i - 1];
            if (prev == null || !IsCleared(prev.levelId)) return false;
            return level.unlockCost <= 0;
        }

        return level.unlockCost <= 0;
    }

    /// <summary>Try to pay a level's unlock cost out of the CollectionTracker.</summary>
    public static bool TryPurchase(LevelData level)
    {
        if (level == null || level.unlockCost <= 0 || level.unlockCurrency == null) return false;
        if (CollectionTracker.Instance == null) return false;
        if (!CollectionTracker.Instance.Spend(level.unlockCurrency, level.unlockCost)) return false;
        MarkUnlocked(level.levelId);
        return true;
    }

    // ---- Housekeeping ----
    public static void ResetAll()
    {
        PlayerPrefs.DeleteAll();
        PlayerPrefs.Save();
        Debug.Log("LevelProgress: all saved progress cleared.");
    }

    public static void ResetLevel(string levelId)
    {
        PlayerPrefs.DeleteKey(ClearedKey + levelId);
        PlayerPrefs.DeleteKey(TimeKey + levelId);
        PlayerPrefs.DeleteKey(CollectKey + levelId);
        PlayerPrefs.DeleteKey(UnlockKey + levelId);
        PlayerPrefs.Save();
    }
}