Scripts/TreeSkills/TreeSkillState.cs
using System;
using Milaine.Interfaces;

namespace Milaine.TreeSkills;

public sealed class TreeSkillState : IManager {
  public static TreeSkillState Instance { get; private set; } = new();

  public List<SkillNodeData> Nodes { get; private set; } = [];
  public HashSet<string> Unlocked { get; private set; } = [];

  public Func<int> GetMoney { get; set; } = () => 0;
  public Func<int> GetSkillPoints { get; set; } = () => 0;

  public Action<int> SpendMoney { get; set; } = amount => { };
  public Action SpendPoint { get; set; } = () => { };

  public Action OnChanged { get; set; }
  public Action<SkillNodeData> OnUnlocked;

  public TreeSkillState() {
    BuildDefaultTree();
  }

  private void BuildDefaultTree() {
    Nodes = [
      // Root Node
      new() {
        Id = "core",
        Name = "milaine.skill.dollars",
        Description = "milaine.skill.dollars.desc",
        KeyDescription = "DollarCategoryValue",
        Cost = 1,
        Category = SkillCategory.Dollars,
        Point = 1,
        Row = 3,
        Column = 3,
        Icon = "🐦‍🔥",
        IsRoot = true
      },

      // branch to top (vertical)
			new() {
        Id = "skill_wallet_1",
        Name = "milaine.skill.wallet",
        Description = "milaine.skill.wallet.desc",
        KeyDescription = "WalletCategoryValue",
        Cost = 600,
        Category = SkillCategory.Wallet,
        Point = 1,
        Row = 2,
        Column = 3,
        Icon = "✊"
      },
      new() {
        Id = "skill_wallet_2",
        Name = "milaine.skill.wallet-v2",
        Description = "milaine.skill.wallet-v2.desc",
        KeyDescription = "WalletCategoryV2Value",
        Cost = 1_000,
        Category = SkillCategory.Wallet,
        Point = 2,
        Row = 1,
        Column = 3,
        Icon = "✊"
      },

			// branch to bottom (vertical)
			new() {
        Id = "skill_dollar_1",
        Name = "milaine.skill.dollars-v2",
        Description = "milaine.skill.dollars-v2.desc",
        KeyDescription = "DollarCategoryV2Value",
        Cost = 20,
        Category = SkillCategory.Dollars,
        Point = 2,
        Row = 4,
        Column = 3,
        Icon = "🫁"
      },
      new() {
        Id = "skill_dollar_2",
        Name = "milaine.skill.dollars-v3",
        Description = "milaine.skill.dollars-v3.desc",
        KeyDescription = "DollarCategoryV3Value",
        Cost = 400,
        Category = SkillCategory.Dollars,
        Point = 6,
        Row = 5,
        Column = 3,
        Icon = "🫁"
      },

			// branch to right (horizontal)
			new() {
        Id = "skill_dividends_1",
        Name = "milaine.skill.dividends",
        Description = "milaine.skill.dividends.desc",
        KeyDescription = "DividendsCategoryValue",
        Cost = 1_000,
        Category = SkillCategory.Dividends,
        Point = 0.02f,
        Row = 3,
        Column = 4,
        Icon = "☕"
      },
      new() {
        Id = "skill_dividends_2",
        Name = "milaine.skill.dividends-v2",
        Description = "milaine.skill.dividends-v2.desc",
        KeyDescription = "DividendsCategoryV2Value",
        Cost = 2_000,
        Category = SkillCategory.Dividends,
        Point = 0.03f,
        Row = 3,
        Column = 5,
        Icon = "☕"
      },

			// branch to left (horizontal)
			new() {
        Id = "skill_investment_1",
        Name = "milaine.skill.investment",
        Description = "milaine.skill.investment.desc",
        KeyDescription = "InvestmentCategoryValue",
        Cost = 600,
        Category = SkillCategory.Investment,
        Point = 1,
        Row = 3,
        Column = 2,
        Icon = "🖐️"
      },
      new() {
        Id = "skill_investment_2",
        Name = "milaine.skill.investment-v2",
        Description = "milaine.skill.investment-v2.desc",
        KeyDescription = "InvestmentCategoryV2Value",
        Cost = 1_000,
        Category = SkillCategory.Investment,
        Point = 2,
        Row = 3,
        Column = 1,
        Icon = "🖐️"
      },

			// branch to diagonal (arriba-derecha)
			new() {
        Id = "skill_walletplus_1",
        Name = "milaine.skill.wallet_plus",
        Description = "milaine.skill.wallet_plus.desc",
        KeyDescription = "WalletPlusCategoryValue",
        Cost = 600,
        Category = SkillCategory.WalletPlus,
        Point = 2,
        Row = 2,
        Column = 4,
        Icon = "🍀"
      },
      new() {
        Id = "skill_walletplus_2",
        Name = "milaine.skill.wallet_plus-v2",
        Description = "milaine.skill.wallet_plus-v2.desc",
        KeyDescription = "WalletPlusCategoryV2Value",
        Cost = 1_000,
        Category = SkillCategory.WalletPlus,
        Point = 2.5f,
        Row = 1,
        Column = 5,
        Icon = "🍀"
      },

			// branch to diagonal (abajo-izquierda) — special node like example
			new() {
        Id = "skill_dollars_magic_1",
        Name = "milaine.skill.magic_dollar",
        Description = "milaine.skill.magic_dollar.desc",
        KeyDescription = "MagicDollarCategoryValue",
        Cost = 20,
        Category = SkillCategory.DollarAutomatic,
        Point = 1,
        Row = 4,
        Column = 2,
        Icon = "⚡"
      },
      new() {
        Id = "skill_dollars_magic_2",
        Name = "milaine.skill.magic_dollar-v2",
        Description = "milaine.skill.magic_dollar-v2.desc",
        KeyDescription = "MagicDollarCategoryV2Value",
        Cost = 100,
        Category = SkillCategory.DollarAutomatic,
        Point = 2,
        Row = 5,
        Column = 1,
        Icon = "⚡"
      },
      new() {
        Id = "skill_dollars_magic_3",
        Name = "milaine.skill.magic_dollar-v3",
        Description = "milaine.skill.magic_dollar-v3.desc",
        KeyDescription = "MagicDollarCategoryV3Value",
        Cost = 600,
        Category = SkillCategory.DollarAutomatic,
        Point = 4,
        Row = 6,
        Column = 0,
        Icon = "⚡"
      },
      new() {
        Id = "skill_dollars_magic_4",
        Name = "milaine.skill.magic_dollar-v4",
        Description = "milaine.skill.magic_dollar-v4.desc",
        KeyDescription = "MagicDollarCategoryV4Value",
        Cost = 1_000,
        Category = SkillCategory.DollarAutomatic,
        Point = 10,
        Row = 7,
        Column = -1,
        Icon = "⚡"
      },
      new() {
        Id = "skill_dollars_magic_5",
        Name = "milaine.skill.magic_dollar-v5",
        Description = "milaine.skill.magic_dollar-v5.desc",
        KeyDescription = "MagicDollarCategoryV5Value",
        Cost = 1_500,
        Category = SkillCategory.DollarAutomatic,
        Point = 20,
        Row = 8,
        Column = -2,
        Icon = "⚡"
      },
    ];
  }

  public SkillNodeData GetNodeAt(int row, int column) => Nodes.FirstOrDefault(n => n.Row == row && n.Column == column);

  public bool IsUnlocked(SkillNodeData node) => node != null && Unlocked.Contains(node.Id);

  public bool CanUnlock(SkillNodeData node) {
    if (node == null || IsUnlocked(node)) {
      return false;
    }

    if (node.IsRoot) {
      return true;
    }

    for (int dr = -1; dr <= 1; dr++) {
      for (int dc = -1; dc <= 1; dc++) {
        if (dr == 0 && dc == 0) {
          continue;
        }

        var neighbor = GetNodeAt(node.Row + dr, node.Column + dc);

        if (neighbor != null && IsUnlocked(neighbor)) {
          return true;
        }
      }
    }

    return false;
  }

  public bool TryUnlock(SkillNodeData node) {
    if (!CanUnlock(node)) {
      return false;
    }

    if (GetMoney() < node.Cost && GetSkillPoints() <= 0) {
      return false;
    }

    if (GetSkillPoints() > 0) {
      SpendPoint();
    } else {
      SpendMoney(node.Cost);
    }

    Unlocked.Add(node.Id);
    OnUnlocked?.Invoke(node);
    OnChanged?.Invoke();

    return true;
  }

  public IEnumerable<SkillNodeData> GetExistingNeighbors(SkillNodeData node) {
    for (int dr = -1; dr <= 1; dr++) {
      for (int dc = -1; dc <= 1; dc++) {
        if (dr == 0 && dc == 0) {
          continue;
        }

        var neighbor = GetNodeAt(node.Row + dr, node.Column + dc);

        if (neighbor != null) {
          yield return neighbor;
        }
      }
    }
  }

  public bool CanPurchase(SkillNodeData node) {
    return GetMoney() >= node.Cost || GetSkillPoints() > 0;
  }

  public void Reset() {
    Unlocked.Clear();
    BuildDefaultTree();
    OnChanged?.Invoke();
  }

  public void Restart() {
    Reset();
  }
}