using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
namespace Ban.Sdaid.Icd.Arch.Tech.Angular
{
/// <summary>
/// ADR-UI-008: Управление состоянием. Signals + injectable-сервисы на трёх уровнях
/// (компонент / страница / приложение), без глобального store.
/// </summary>
public class ADR_UI_008_State : IAdrDocument, IFolder<ArchAngularFolder>
{
public string Name => "ADR-UI-008. Управление состоянием";
public string Description =>
@"Состояние живёт на нескольких уровнях: локальное (компонент), страничное (несколько
компонентов одной страницы), разделяемое (несколько частей приложения). Фиксируем один способ —
signals + injectable-сервисы, без параллельных подходов и без глобального store.";
public string Version => "1.0";
public string Status => "accepted";
public string[] Comments => new[]
{
"2026-08-11. Принято перед реализацией состояния страницы «Ввод данных».",
};
public Type? Supersedes => null;
public static string S1_Context = """
## Контекст и постановка задачи
Состоянием надо управлять на трёх уровнях:
- **локальное** — внутри компонента (текущий шаг, открытость блока, прогресс);
- **страничное** — общее для нескольких компонентов одной страницы;
- **разделяемое** — нужное нескольким частям приложения (профиль, конфиг).
Нужен **единый способ** — без смеси signals / `BehaviorSubject` / глобального store.
""";
public static string S2_DecisionDrivers = """
## Драйверы решения
1. **Минимум абстракций** — требований к undo/time-travel/оффлайну нет; глобальный store избыточен.
2. **Явный поток данных** — компонент → сервис → API; без неявных глобальных «магнитов».
3. **Нативные средства Angular** — signals + `computed()` + `toSignal()` покрывают почти всё.
4. **Локальность** — состояние живёт там, где используется; удаление фичи удаляет её состояние.
""";
public static string S3_ConsideredOptions = """
## Рассмотренные варианты
- **A. Глобальный store** (NgRx / elf) — универсально, но избыточно; лишняя зависимость.
- **B. Signals + injectable-сервисы** — локальное в сигналах компонента; разделяемое — в
`providedIn:'root'`-сервисе с сигналами.
- **C. RxJS Subjects во всём** — проигрывает signals по эргономике в шаблонах и `computed`.
""";
public static string S4_DecisionOutcome = """
## Решение
Выбран **Вариант B**: signals + injectable-сервисы.
### Уровни состояния
| Уровень | Где живёт | Срок жизни |
|---|---|---|
| Компонентное | Сигналы внутри компонента | жизнь компонента |
| Страничное | Per-page service в `providers` страницы (ADR-UI-013) | жизнь страницы |
| Разделяемое | `@Injectable({ providedIn: 'root' })` сервис с сигналами | жизнь приложения |
### Компонентное состояние
- `signal()` / `computed()` внутри компонента; изменения только через `set()` / `update()` (не
мутируем объект).
- Производное — через `computed()`, не копией в нескольких сигналах.
### Страничное состояние
- Нетривиальная страница имеет **per-page service**, зарегистрированный в `providers` страницы
(живёт ровно пока активна страница).
- Внутри — сигналы; компонент и под-компоненты читают один экземпляр через `inject(...)`.
- Данные из API кладутся в сигнал сервиса (или компонент подписывается через
`takeUntilDestroyed()` и кладёт в свой сигнал).
### Разделяемое состояние
- Только для данных, реально нужных нескольким частям (профиль, конфиг).
- `@Injectable({ providedIn: 'root' })` в `src/app/services/`.
- Контракт — **сигналы для чтения** (`asReadonly()`) + **методы для записи**. Прямая запись в
сигнал извне запрещена (private signal + публичный readonly).
### Подписки и Observable
- Угасание потоков — `takeUntilDestroyed()` + `DestroyRef` (без `until-destroy`, ADR-UI-001).
- Observable → Signal — `toSignal(stream$)`, по возможности с явным `initialValue`.
### Чего не делаем
- **Глобальный store** (NgRx/elf и аналоги).
- **Запись через мутацию** — только `set`/`update`.
- **Скрытые синглтоны** (`static`-поля, модули-синглтоны как имитация store) — разделяемое
всегда в инжектируемом сервисе.
- **Локальный store на странице через RxJS Subject** — замена: per-page service с сигналами.
""";
public static string S5_PositiveConsequences = """
## Положительные следствия
- Один способ работы со состоянием — меньше развилок для разработчика и ИИ.
- Сигналы интегрированы с Angular (change detection, шаблоны, `computed`) без лишних операторов.
- Удаление страницы/фичи механически удаляет её состояние.
""";
public static string S6_NegativeConsequences = """
## Отрицательные следствия и компромиссы
- Нет time-travel / Redux DevTools — при появлении требований к сложной отладке заведём
отдельный ADR на store.
- Оптимистичные обновления/откат реализуются вручную в сервисе.
""";
public static string S7_Validation = """
## Проверка
- В коде нет импортов глобального store.
- Компоненты с локальным состоянием используют сигналы (нет состояния в полях класса вне
сигналов и форм).
- Разделяемое состояние читается через `inject(<Service>).<signal>()` и пишется только методами
сервиса.
""";
public static string S8_OpenQuestions = """
## Открытые вопросы / отложено
- **Глобальный store** — пересмотрим при требованиях к time-travel / оффлайну / синхронизации вкладок.
- **Кэширование данных между страницами** (справочники) — пока per-page; общий уровень — отдельным ADR.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Arch.Tech.Angular.ADR_UI_001_Stack)} — signals + сервисы, без store.
- {nameof(Ban.Sdaid.Icd.Arch.Tech.Angular.ADR_UI_009_Forms)} — форма как источник значений-сигналов.
""";
}
}