using Theriapolis.Core.Data;
using Theriapolis.Core.Entities;
using Theriapolis.Core.Items;
using Theriapolis.Core.Rules.Reputation;
using Theriapolis.Core.Time;
using Theriapolis.Core.World;
using Theriapolis.Core.World.Settlements;

namespace Theriapolis.Core.Rules.Quests;

/// <summary>
/// Phase 6 M4 — read/write window the QuestEngine uses to evaluate
/// conditions and apply effects. Deliberately holds references rather
/// than copies so engine ticks see live state.
/// </summary>
public sealed class QuestContext
{
    public ContentResolver         Content      { get; }
    public ActorManager            Actors       { get; }
    public PlayerReputation        Reputation   { get; }
    public Dictionary<string, int> Flags        { get; }
    public AnchorRegistry          Anchors      { get; }
    public WorldClock              Clock        { get; }
    public WorldState              World        { get; }
    public Rules.Character.Character? PlayerCharacter { get; set; }

    /// <summary>Most recent dialogue node id reached, surfaced by InteractionScreen for the dialogue_choice condition.</summary>
    public string LastDialogueNodeReached { get; set; } = "";

    public QuestContext(ContentResolver content, ActorManager actors,
                        PlayerReputation rep, Dictionary<string, int> flags,
                        AnchorRegistry anchors, WorldClock clock, WorldState world)
    {
        Content    = content;
        Actors     = actors;
        Reputation = rep;
        Flags      = flags;
        Anchors    = anchors;
        Clock      = clock;
        World      = world;
    }

    public bool HasItem(string itemId)
    {
        if (PlayerCharacter is null) return false;
        foreach (var inst in PlayerCharacter.Inventory.Items)
            if (string.Equals(inst.Def.Id, itemId, System.StringComparison.OrdinalIgnoreCase))
                return true;
        return false;
    }

    /// <summary>
    /// Position of the live player actor in tactical-tile (= world-pixel)
    /// space, or null if the actor isn't yet spawned.
    /// </summary>
    public (int x, int y)? PlayerTacticalPos()
    {
        if (Actors.Player is null) return null;
        return ((int)Actors.Player.Position.X, (int)Actors.Player.Position.Y);
    }

    /// <summary>
    /// World-tile Chebyshev distance from the player to the settlement
    /// with the given id, or null if the player or settlement isn't
    /// resolvable.
    /// </summary>
    public int? PlayerDistanceToSettlement(int settlementId)
    {
        if (Actors.Player is null) return null;
        Settlement? s = null;
        foreach (var sx in World.Settlements)
            if (sx.Id == settlementId) { s = sx; break; }
        if (s is null) return null;
        int playerWX = (int)Actors.Player.Position.X / C.WORLD_TILE_PIXELS;
        int playerWY = (int)Actors.Player.Position.Y / C.WORLD_TILE_PIXELS;
        int dx = System.Math.Abs(playerWX - s.TileX);
        int dy = System.Math.Abs(playerWY - s.TileY);
        return System.Math.Max(dx, dy);
    }
}
