# Задача: Unity / C# Middle

## Кратко

Нужно реализовать загрузочный поток приложения в Unity через собственную state machine.

Проект создается с нуля. Базовые абстракции, сервисы, View/ViewModel и state machine нужно написать самостоятельно, потому что это часть оценки.

Ориентировочное время выполнения: около 1,5 часов. Если ушло больше или меньше времени, это нормально, но нужно честно указать это в документации.

## Технические требования

- Unity 2022 LTS+.
- C#.
- Использовать VContainer, UniTask и UniRx.
- Опционально можно использовать Odin Inspector и DOTween.

## Архитектурные требования

### Reactive

Использовать UniRx для реактивных значений и подписок.

Если дополнительно понадобится собственная обертка `ReactiveValue<T>`, она должна поддерживать сигнатуру подписки:

```csharp
IDisposable Subscribe(Action<T> cb, bool invokeImmediately = true)
```

Требования:

- каждая подписка должна корректно диспозиться;
- нельзя использовать голые `event Action` без отписки.

### Async

Все async-операции должны быть реализованы только через UniTask и `CancellationToken`.

Запрещено использовать `async void`, кроме Unity-колбэков.

### Dependency Injection

Использовать VContainer.

Все сервисы регистрируются как интерфейсы:

```csharp
builder.Register<Impl>(Lifetime.Singleton).As<IInterface>();
```

Запрещено использовать:

- `FindObjectOfType`;
- `Singleton.Instance`;
- `static`-хранилища состояния.

### UI

Нужно сделать базовую пару View/ViewModel:

```csharp
public class UIView : MonoBehaviour
{
    public virtual void Initialize() { }
    public virtual void Release() { }
}

public class UIView<TVm> : UIView where TVm : IUIViewModel
{
}
```

Требования:

- ViewModel должна быть обычным C#-классом, не `MonoBehaviour`;
- логика находится во ViewModel;
- View хранит только Unity-ссылки и биндинги.

### Services

Сервисы должны иметь асинхронный жизненный цикл:

```csharp
UniTask InitializeAsync(CancellationToken ct)
UniTask ReleaseAsync(CancellationToken ct)
```

Требования:

- сервисы оформляются как `Service : IService`;
- фоновые циклы стартуют в `InitializeAsync`;
- фоновые циклы останавливаются через `CancellationTokenSource.Cancel()` в `ReleaseAsync`.

### Config

Конфиги должны быть `ScriptableObject` с суффиксом `*Settings`.

Регистрация в `LifetimeScope`:

```csharp
[SerializeField] private SomeSettings _settings;

protected override void Configure(IContainerBuilder builder)
{
    builder.RegisterInstance(_settings);
}
```

## Задача: Boot Flow

Реализовать загрузочный поток приложения через собственную state machine из трех состояний.

### 1. База state machine

Нужно реализовать собственные абстракции:

- `IState`;
- `IStatesController<TEnum>`;
- `StatesController<TEnum>`.

В `StatesController<TEnum>` должен быть метод:

```csharp
UniTask EnterStateAsync(TEnum code, CancellationToken ct)
```

Контракт перехода между состояниями:

```csharp
await currentState.ExitAsync(ct);
await newState.EnterAsync(ct);
```

Требования:

- `CancellationToken` пробрасывается через всю цепочку вызовов;
- `CancellationToken` должен реально отменять внутренние ожидания;
- повторные входы в состояния не должны ломать UI и подписки.

### 2. Состояния

Нужно реализовать три состояния.

#### SplashState

Поведение:

- показывает лого;
- ждет 1 секунду через `UniTask.Delay(..., ct)`;
- переходит в следующее состояние.

#### LoadState

Поведение:

- имитирует загрузку;
- выполняет 5 шагов по 200 мс;
- хранит `ReactiveValue<float> Progress` со значением от `0` до `1`;
- обновляет `Progress` после каждого шага.

#### MenuState

Поведение:

- показывает `MenuUIView`;
- содержит одну кнопку `Restart`;
- по клику на `Restart` возвращает приложение в `LoadState`.

### 3. UI

Нужно реализовать `LoadingUIView`.

Требования:

- `LoadingUIView` подписывается на `LoadState.Progress`;
- прогресс-бар обновляется через DOTween или ручной Lerp;
- при `ExitAsync` все подписки должны корректно очищаться;
- при повторном входе в `LoadState` не должно быть `NullReferenceException` и дублирующихся подписок.

## Что сдать

- GitHub-репозиторий или zip-архив без `Library/`, `Temp/`, `obj/`.
- `README.md` с инструкцией запуска.
- В `README.md` добавить 5-10 строк о том, что было бы доделано при наличии еще 2 часов.
- `SELF_NOTES.md` с описанием собственных решений.
- `AI_LOG.md` с описанием использования AI. Если AI не использовался, нужно написать почему.

## SELF_NOTES.md

В `SELF_NOTES.md` нужно своими словами описать:

- какие идеи были рассмотрены и почему выбрана текущая реализация;
- какие места в коде были придуманы и написаны самостоятельно, без AI;
- что в коде понятно до последней строки;
- что осталось непонятным или воспринимается как магия;
- ответы на 2-3 ключевых вопроса вида: почему здесь сделано именно так.

## AI_LOG.md

В `AI_LOG.md` нужно описать:

- какие промпты использовались;
- где AI ошибся;
- что было переписано руками;
- если AI не использовался, почему было принято такое решение.

## Что не требуется

Не нужно тратить время на:

- красивый арт;
- сложные анимации;
- звук;
- мобильную сборку.

Главное: архитектурные слои, стиль кода, корректная работа жизненного цикла и понимание собственных решений.
