using System.ComponentModel.DataAnnotations;
using ERPCore.Domain.Enums;

namespace ERPCore.Dtos.Production;

// Production template contract (docs/30-BACKEND-PHASE2.md §D.1).
//
// The `Key` vocabulary: every stage and every output carries a client-facing string key
// alongside its database id. On a GET the key IS the stringified id; on a save the client
// echoes those keys back for rows it kept and mints "tmp-<uuid>" keys for rows it just
// drew. Edges and Upstream inputs then reference stages/outputs *by key* only, which is
// what lets one payload shape — and one validator — serve both POST (nothing has an id
// yet) and PUT (most things do).

// --- responses ---------------------------------------------------------------

/// <summary>
/// Row on the template list (docs/30 §D.1 <c>GET /production-templates</c>).
/// </summary>
/// <remarks>
/// <c>StageNames</c> is an addition to the documented shape. The template overview is a
/// canvas showing every template as a production line with its stages left-to-right
/// (docs/21 §1), so it needs the names for every listed row — without them the client would
/// have to fetch each template's full graph just to label the boxes. Ordered by stage id,
/// matching the graph endpoint.
/// </remarks>
public sealed record TemplateSummaryDto(
    int TemplateId, string Code, string Name, EntityStatus Status,
    int StageCount, IReadOnlyList<string> StageNames,
    int ActiveRunCount, int CreatedBy, DateTime CreatedAt);

/// <summary>One custom field definition, serialized verbatim into the stage's <c>field_defs</c> jsonb.</summary>
public sealed record FieldDefDto(
    string Key, string Label, CustomFieldType Type, IReadOnlyList<string>? Options, bool Required);

public sealed record StageInputDto(
    int InputId, StageInputSource Source, int? ItemId,
    int? FromOutputId, string? FromOutputKey, StageQtyUnit QtyUnit, decimal QtyPerBatch);

/// <summary><c>UomId</c> is the WIP label and is null exactly when <c>ItemId</c> is set.</summary>
public sealed record StageOutputDto(
    int OutputId, string Key, int? ItemId, string Name, int? UomId, decimal QtyPerBatch);

public sealed record TemplateStageDto(
    int StageId, string Key, string Name, string? RoleLabel, int EstimatedMinutes,
    decimal PosX, decimal PosY, IReadOnlyList<FieldDefDto> FieldDefs,
    IReadOnlyList<StageInputDto> Inputs, IReadOnlyList<StageOutputDto> Outputs);

public sealed record TemplateEdgeDto(
    int EdgeId, int ParentStageId, int ChildStageId, string ParentKey, string ChildKey);

/// <summary>
/// A canvas-only grouping box or divider line. Round-tripped verbatim: no server-side
/// meaning whatsoever, and invisible to the graph validator.
/// </summary>
public sealed record CanvasAnnotationDto(
    string Kind, decimal PosX, decimal PosY, decimal Width, decimal Height,
    string? Label, decimal? Rotation);

/// <summary>Full graph (docs/30 §D.1 <c>GET /production-templates/{id}</c>).</summary>
/// <remarks>
/// <c>ActiveRunCount</c> and <c>Annotations</c> are additions to the documented shape.
/// The first is what puts the builder into its edit-locked state (docs/21 §2) and mirrors
/// the condition <c>UpdateAsync</c> enforces — without it the builder would need a second
/// request to the list endpoint just to know whether to disable itself.
/// </remarks>
public sealed record TemplateGraphDto(
    int TemplateId, string Code, string Name, string? Description, EntityStatus Status,
    IReadOnlyList<TemplateStageDto> Stages, IReadOnlyList<TemplateEdgeDto> Edges,
    IReadOnlyList<CanvasAnnotationDto> Annotations, int ActiveRunCount,
    int CreatedBy, DateTime CreatedAt);

// --- requests ----------------------------------------------------------------
// Narrow by design (02-SECURITY §B.6): no status, no ids, no createdBy, no timestamps.
// POST and PUT share this shape; PUT replaces the whole graph.

public sealed class SaveTemplateRequest
{
    [Required, StringLength(30, MinimumLength = 1)]
    public string Code { get; set; } = string.Empty;

    [Required, StringLength(150, MinimumLength = 1)]
    public string Name { get; set; } = string.Empty;

    [StringLength(500)]
    public string? Description { get; set; }

    [Required, MinLength(1)]
    public List<SaveStageRequest> Stages { get; set; } = new();

    /// <summary>Empty is legal — a single-stage template is both entry and terminal.</summary>
    public List<SaveEdgeRequest> Edges { get; set; } = new();

    /// <summary>
    /// Canvas boxes/lines. Capped because this is free-form client state going into a jsonb
    /// column — a hand-rolled request should not be able to store an unbounded blob.
    /// </summary>
    [MaxLength(200)]
    public List<CanvasAnnotationDto> Annotations { get; set; } = new();
}

public sealed class SaveStageRequest
{
    /// <summary>Existing stage id as a string, or a client-minted <c>tmp-*</c> key for a new stage.</summary>
    [Required, StringLength(60, MinimumLength = 1)]
    public string Key { get; set; } = string.Empty;

    [Required, StringLength(150, MinimumLength = 1)]
    public string Name { get; set; } = string.Empty;

    [StringLength(60)]
    public string? RoleLabel { get; set; }

    [Range(0, 1_000_000)]
    public int EstimatedMinutes { get; set; }

    /// <summary>Canvas coordinates, stored verbatim and never interpreted server-side.</summary>
    public decimal PosX { get; set; }
    public decimal PosY { get; set; }

    public List<FieldDefDto> FieldDefs { get; set; } = new();
    public List<SaveInputRequest> Inputs { get; set; } = new();
    public List<SaveOutputRequest> Outputs { get; set; } = new();
}

public sealed class SaveInputRequest
{
    [Required]
    public StageInputSource Source { get; set; }

    /// <summary>Required when <see cref="Source"/> is Stock; must be null when Upstream.</summary>
    public int? ItemId { get; set; }

    /// <summary>Required when <see cref="Source"/> is Upstream; must name an output of a direct parent.</summary>
    [StringLength(60)]
    public string? FromOutputKey { get; set; }

    /// <summary>
    /// What <see cref="QtyPerBatch"/> means. <c>Content</c> (ml/g) is allowed only on a Stock
    /// input whose item has a content size; Upstream inputs must be <c>Pack</c>.
    /// </summary>
    [EnumDataType(typeof(StageQtyUnit))]
    public StageQtyUnit QtyUnit { get; set; } = StageQtyUnit.Pack;

    [Range(0.0001, double.MaxValue)]
    public decimal QtyPerBatch { get; set; }
}

public sealed class SaveOutputRequest
{
    /// <summary>Existing output id as a string, or a <c>tmp-*</c> key. Referenced by Upstream inputs.</summary>
    [Required, StringLength(60, MinimumLength = 1)]
    public string Key { get; set; } = string.Empty;

    /// <summary>Required on the terminal stage's single output; must be null on every other output.</summary>
    public int? ItemId { get; set; }

    [Required, StringLength(150, MinimumLength = 1)]
    public string Name { get; set; } = string.Empty;

    /// <summary>
    /// The WIP display unit. Required when <see cref="ItemId"/> is null; must be null when it
    /// is set, because a real item's unit is its own base UOM.
    /// </summary>
    [Range(1, int.MaxValue)]
    public int? UomId { get; set; }

    [Range(0.0001, double.MaxValue)]
    public decimal QtyPerBatch { get; set; }
}

public sealed class SaveEdgeRequest
{
    [Required, StringLength(60, MinimumLength = 1)]
    public string ParentKey { get; set; } = string.Empty;

    [Required, StringLength(60, MinimumLength = 1)]
    public string ChildKey { get; set; } = string.Empty;
}

/// <summary>Body of <c>PATCH /production-templates/{id}/status</c> (docs/30 §D.1).</summary>
public sealed class UpdateTemplateStatusRequest
{
    [Required]
    public EntityStatus Status { get; set; }
}
