using System;
using System.Collections.Generic;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd.Domain.Capture;
using Ban.Sdaid.Icd.Domain.Cards.Template;
namespace Ban.Sdaid.Icd.Domain.Cards
{
/// <summary>Документ информационной карты — то, что заполнено по своему виду; может быть составным.</summary>
[DomainEntity]
public class CardDocument
{
/// <summary>Идентификатор документа.</summary>
public Guid DocId { get; init; }
/// <summary>Непосредственный родитель; пуст у документа верхнего уровня.</summary>
public Guid? ParentId { get; init; }
/// <summary>Вид, по которому заполнен документ.</summary>
[Relation]
public DocumentKind Kind { get; init; } = null!;
/// <summary>Вложенные документы.</summary>
[Relation(Kind = RelationKind.Composition)]
public IReadOnlyList<CardDocument> NestedDocuments { get; init; } = new CardDocument[0];
/// <summary>Заполненные атрибуты документа.</summary>
[Relation(Kind = RelationKind.Composition)]
public IReadOnlyList<CardDocumentAttribute> Attributes { get; init; } = new CardDocumentAttribute[0];
}
/// <summary>Атрибут документа карты — факт заполнения атрибута вида в конкретном документе.</summary>
[DomainEntity]
public class CardDocumentAttribute
{
/// <summary>Документ, которому принадлежит заполненный атрибут.</summary>
[Relation]
public CardDocument Document { get; init; } = null!;
/// <summary>Атрибут вида документа, который заполнен.</summary>
[Relation]
public DocumentKindAttribute KindAttribute { get; init; } = null!;
/// <summary>Значения в текстовом представлении; для выбора — коды вариантов. Пусто — не заполнено.</summary>
public IReadOnlyList<string> Values { get; init; } = new string[0];
/// <summary>Происхождение значения: распознано или введено пользователем.</summary>
[Relation]
public ValueOrigin Origin { get; init; }
/// <summary>Пользователь, заполнивший значение.</summary>
public Guid FilledByUserId { get; init; }
/// <summary>Момент заполнения.</summary>
public DateTime FilledAt { get; init; }
}
/// <summary>Описание доменной сущности <see cref="CardDocument"/>.</summary>
public class CardDocumentSpec : IDomainEntityDocument
{
public static string _refName = "документ информационной карты";
public Type Entity => typeof(CardDocument);
public string Name => "Документ информационной карты";
public string Description =>
@"То, что заполнено на конкретной карте: документ своего вида с заполненными атрибутами и вложенными документами.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new[]
{
"2026-08-19. Заведён по варианту 5 решения о структуре данных; заместил документ со значениями атрибутов.",
};
public static string S1_Pairing = """
## Пара к описанию
Состав задан дважды параллельными парами. **Вид документа** и его **атрибут вида** —
описание того, что бывает; **документ карты** и его **атрибут документа** — то, что
заполнено на конкретной карте.
Каждый уровень экземпляра ссылается на свой уровень описания: документ знает свой вид,
атрибут документа знает атрибут вида. Через атрибут вида известны обязательность,
код и наименование атрибута, его тип значения, порядок и участие в ядре.
""";
public static string S2_Nesting = """
## Вложение
Документ ссылается на непосредственного родителя, образуя дерево заполненных документов
внутри карты: эксперты, замеры величины pH, авторы, переписчики, владельцы, биотесты,
примечания.
Дерево экземпляров повторяет дерево видов: документ может быть вложен в другой, только
если его вид входит в состав вида родителя.
""";
public static string S3_Invariants = """
## Инварианты
- Вид документа не меняется после создания: смена вида — это другой документ.
- Атрибут документа ссылается на атрибут **своего** вида; чужой атрибут вида в документ
попасть не может.
- Один атрибут вида заполняется в документе не более одного раза: повтор выражается
не вторым заполнением, а списком значений либо вложенным документом.
- Документ полон, когда заполнены все обязательные атрибуты его вида.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Arch.Adr.ADR_006_CardDataStructure)} — решение о структуре данных информационной карты
""";
}
/// <summary>Описание доменной сущности <see cref="CardDocumentAttribute"/>.</summary>
public class CardDocumentAttributeSpec : IDomainEntityDocument
{
public static string _refName = "атрибут документа карты";
public Type Entity => typeof(CardDocumentAttribute);
public string Name => "Атрибут документа карты";
public string Description =>
@"Заполнение атрибута в документе карты: значения, происхождение, кем и когда заполнено.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new string[0];
public static string S1_Purpose = """
## Назначение
Здесь живёт **факт заполнения** — то, что не относится ни к описанию, ни к самому
значению: кем и когда заполнено, распознано или введено вручную. В варианте без этой
сущности такие сведения пришлось бы вешать либо на значение, либо на документ целиком,
теряя привязку к полю.
Значение — **поле** этой сущности, а не отдельная сущность: собственных реквизитов
у него нет. Уверенность распознавания, кадр и область остались в пакете ввода, потому
что это данные о распознавании, а не о результате.
""";
public static string S2_Values = """
## Значения
Значения хранятся списком: тип может допускать несколько. Материалы основы, украшения
и физико-механические повреждения отмечаются сразу несколькими клетками.
Цена такого хранения — запрос «во всех картах, где среди материалов есть картон»
становится поиском по содержимому списка, а не соединением таблиц. Это приемлемо:
поиск за пределами ядра карты решено не поддерживать.
Значения хранятся текстом, а правило их разбора известно из типа значения атрибута.
Для выбора из перечисления и справочника хранятся коды вариантов, а не подписи:
подпись зависит от бланка, код — нет.
""";
}
}