using System.Text.Json.Serialization;
using NeonSprawl.Server.Game.PositionState;
namespace NeonSprawl.Server.Game.Targeting;
/// JSON body for GET /game/players/{{id}}/target and nested targetState on select (NEO-23).
public sealed class PlayerTargetStateResponse
{
public const int CurrentSchemaVersion = 1;
[JsonPropertyName("schemaVersion")]
public int SchemaVersion { get; init; } = CurrentSchemaVersion;
[JsonPropertyName("playerId")]
public required string PlayerId { get; init; }
/// Lowercase registry id when locked; JSON null when no lock (key always serialized).
[JsonPropertyName("lockedTargetId")]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public string? LockedTargetId { get; init; }
/// One of string constants.
[JsonPropertyName("validity")]
public required string Validity { get; init; }
[JsonPropertyName("sequence")]
public int Sequence { get; init; }
}
/// POST body for POST /game/players/{{id}}/target/select (NEO-23).
///
/// Omit targetId or send JSON null to clear the lock. Whitespace-only after trim → HTTP 400.
///
public sealed class TargetSelectRequest
{
public const int CurrentSchemaVersion = 1;
[JsonPropertyName("schemaVersion")]
public int SchemaVersion { get; init; }
/// Registry key after trim + case-insensitive lookup; null or omitted ⇒ clear intent.
[JsonPropertyName("targetId")]
public string? TargetId { get; init; }
///
/// Optional client-reported current position. When present the server uses these coordinates
/// for the range check instead of the stored PositionState snapshot, eliminating the
/// race between move-stream and target/select POSTs (NEO-24 follow-up #5). Purely
/// advisory: it does not write to the position store (move-stream remains the
/// only write path). Real combat (E5.M1) will bound the hint against the stored snap / movement
/// budget so this cannot be used to "teleport" the radius check — prototype does not yet.
///
[JsonPropertyName("positionHint")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public PositionVector? PositionHint { get; init; }
}
/// POST response for target selection (NEO-23).
public sealed class TargetSelectResponse
{
public const int CurrentSchemaVersion = 1;
[JsonPropertyName("schemaVersion")]
public int SchemaVersion { get; init; } = CurrentSchemaVersion;
[JsonPropertyName("selectionApplied")]
public bool SelectionApplied { get; init; }
[JsonPropertyName("targetState")]
public required PlayerTargetStateResponse TargetState { get; init; }
/// Required when is false; omitted on success.
[JsonPropertyName("reasonCode")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? ReasonCode { get; init; }
}