using System.Text.Json.Serialization;

namespace Theriapolis.Core.Data;

/// <summary>
/// Immutable biome definition loaded from biomes.json.
/// Defines the biome's identity, visual representation, and the (e,m,t) ranges
/// that can produce it during BiomeAssign.
/// </summary>
public sealed record BiomeDef
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    [JsonPropertyName("display_name")]
    public string DisplayName { get; init; } = "";

    /// <summary>Single capital letter used in placeholder tile rendering.</summary>
    [JsonPropertyName("letter")]
    public char Letter { get; init; } = '?';

    /// <summary>Hex color string (#RRGGBB) for the placeholder tile background.</summary>
    [JsonPropertyName("color")]
    public string Color { get; init; } = "#888888";

    [JsonPropertyName("placeholder_sprite")]
    public string PlaceholderSprite { get; init; } = "";

    // ── Assignment thresholds ────────────────────────────────────────────────
    // These are used only for "natural" biome assignment.
    // Ocean is handled separately (elevation < sea_level).
    [JsonPropertyName("elevation_min")]  public float ElevationMin  { get; init; } = 0f;
    [JsonPropertyName("elevation_max")]  public float ElevationMax  { get; init; } = 1f;
    [JsonPropertyName("moisture_min")]   public float MoistureMin   { get; init; } = 0f;
    [JsonPropertyName("moisture_max")]   public float MoistureMax   { get; init; } = 1f;
    [JsonPropertyName("temp_min")]       public float TempMin       { get; init; } = 0f;
    [JsonPropertyName("temp_max")]       public float TempMax       { get; init; } = 1f;

    /// <summary>Priority — higher-priority biomes win when multiple match.</summary>
    [JsonPropertyName("priority")]
    public int Priority { get; init; } = 0;

    /// <summary>True if this is a transition/mixed biome (not assignable from base rules).</summary>
    [JsonPropertyName("is_transition")]
    public bool IsTransition { get; init; } = false;

    // ── Parsed color cache ───────────────────────────────────────────────────
    private (byte R, byte G, byte B)? _parsedColor;

    public (byte R, byte G, byte B) ParsedColor()
    {
        if (_parsedColor.HasValue) return _parsedColor.Value;
        string hex = Color.TrimStart('#');
        byte r = Convert.ToByte(hex[..2], 16);
        byte g = Convert.ToByte(hex[2..4], 16);
        byte b = Convert.ToByte(hex[4..6], 16);
        _parsedColor = (r, g, b);
        return _parsedColor.Value;
    }

    /// <summary>How well this biome matches the given (e, m, t) values. Returns 0 if outside range.</summary>
    public float Score(float e, float m, float t)
    {
        if (e < ElevationMin || e > ElevationMax) return 0f;
        if (m < MoistureMin  || m > MoistureMax)  return 0f;
        if (t < TempMin      || t > TempMax)       return 0f;

        // Score = how close the values are to the center of the range (prefer tighter fits)
        float eMid = (ElevationMin + ElevationMax) * 0.5f;
        float mMid = (MoistureMin  + MoistureMax)  * 0.5f;
        float tMid = (TempMin      + TempMax)       * 0.5f;

        float eHalf = (ElevationMax - ElevationMin) * 0.5f + 0.001f;
        float mHalf = (MoistureMax  - MoistureMin)  * 0.5f + 0.001f;
        float tHalf = (TempMax      - TempMin)       * 0.5f + 0.001f;

        float closeness = 1f - (MathF.Abs(e - eMid)/eHalf + MathF.Abs(m - mMid)/mHalf + MathF.Abs(t - tMid)/tHalf) / 3f;
        return closeness + Priority * 0.5f;
    }
}
