using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Rendering;
using PlotLine.Models;

namespace PlotLine.ViewModels;

public sealed class ProjectEditViewModel
{
    public int ProjectID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Project / Series name")]
    public string ProjectName { get; set; } = string.Empty;

    public string? Description { get; set; }

    [Display(Name = "Genre / Metric Preset")]
    public string GenreMetricPresetKey { get; set; } = "Custom";

    public IReadOnlyList<SelectListItem> GenreMetricPresetOptions { get; set; } = [];
}

public sealed class ProjectIndexViewModel
{
    public IReadOnlyList<ProjectIndexCardViewModel> ActiveProjects { get; init; } = [];
    public IReadOnlyList<ProjectIndexCardViewModel> ArchivedProjects { get; init; } = [];
    public TrialVisibilityViewModel? Trial { get; init; }
    public OnboardingNudgeViewModel OnboardingNudge { get; init; } = new();
    public StoryIntelligenceDashboardViewModel StoryIntelligence { get; init; } = new();
    public bool HasAnyProjects => ActiveProjects.Any() || ArchivedProjects.Any();
}

public sealed class ProjectIndexCardViewModel
{
    public int ProjectID { get; set; }
    public string ProjectName { get; set; } = string.Empty;
    public string? Description { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime UpdatedDate { get; set; }
    public bool IsArchived { get; set; }
    public DateTime? ArchivedDate { get; set; }
    public string AccessRole { get; set; } = "Owner";
    public bool IsOwner => string.Equals(AccessRole, "Owner", StringComparison.OrdinalIgnoreCase);
    public int BookCount { get; set; }
    public int TotalWords { get; set; }
    public DateTime? LastBookUpdatedDate { get; set; }
    public IReadOnlyList<string> CoverThumbnailPaths { get; set; } = [];
}

public sealed class ProjectDetailViewModel
{
    public Project Project { get; set; } = new();
    public ProjectDashboardSummaryViewModel Summary { get; set; } = new();
    public ProjectDashboardSeriesProgressViewModel SeriesProgress { get; set; } = new();
    public IReadOnlyList<ProjectDashboardActivityItemViewModel> RecentActivity { get; set; } = [];
    public ProjectDashboardAttentionViewModel NeedsAttention { get; set; } = new();
    public IReadOnlyList<ProjectDashboardBookCardViewModel> Books { get; set; } = [];
}

public sealed class ProjectDashboardSummaryViewModel
{
    public int TotalBooks { get; set; }
    public int PublishedBooks { get; set; }
    public int DraftingBooks { get; set; }
    public int PlanningBooks { get; set; }
    public int TotalWords { get; set; }
    public int CharacterCount { get; set; }
    public int LocationCount { get; set; }
    public int ActivePlotThreadCount { get; set; }
    public int ContinuityWarningCount { get; set; }
}

public sealed class ProjectDashboardBookCardViewModel
{
    public int BookID { get; set; }
    public int BookNumber { get; set; }
    public string BookTitle { get; set; } = string.Empty;
    public string? Subtitle { get; set; }
    public string Status { get; set; } = BookStatuses.Planning;
    public string StatusCssClass { get; set; } = "dashboard-status-planning";
    public int CurrentWordCount { get; set; }
    public int? TargetWordCount { get; set; }
    public decimal? ProgressPercentage { get; set; }
    public int ChapterCount { get; set; }
    public int SceneCount { get; set; }
    public DateTime UpdatedDate { get; set; }
    public string CoverThumbnailPath { get; set; } = "/images/placeholders/book-cover.svg";

    public string WordCountLabel => TargetWordCount is > 0
        ? $"{CurrentWordCount:N0} / {TargetWordCount.Value:N0} words"
        : $"{CurrentWordCount:N0} words";
}

public sealed class ProjectDashboardSeriesProgressViewModel
{
    public decimal Percentage { get; set; }
    public string SupportingLine { get; set; } = string.Empty;
    public string CalculationLabel { get; set; } = string.Empty;
}

public sealed class ProjectDashboardActivityItemViewModel
{
    public string ActivityType { get; set; } = string.Empty;
    public string EntityType { get; set; } = string.Empty;
    public string? EntityName { get; set; }
    public string? Description { get; set; }
    public string UserLabel { get; set; } = string.Empty;
    public string TimeLabel { get; set; } = string.Empty;
    public string IconLabel { get; set; } = string.Empty;
}

public sealed class ProjectDashboardAttentionViewModel
{
    public IReadOnlyList<ProjectDashboardWarningItemViewModel> Warnings { get; set; } = [];
    public IReadOnlyList<ProjectDashboardThreadItemViewModel> Threads { get; set; } = [];
    public IReadOnlyList<ProjectDashboardOverdueBookViewModel> OverdueBooks { get; set; } = [];
    public IReadOnlyList<ProjectDashboardBookAttentionItemViewModel> BookItems { get; set; } = [];
    public bool ShowMissingSchedulePrompt { get; set; }

    public bool HasItems => Warnings.Any() || Threads.Any() || OverdueBooks.Any() || BookItems.Any() || ShowMissingSchedulePrompt;
}

public sealed class ProjectDashboardWarningItemViewModel
{
    public string SeverityName { get; set; } = string.Empty;
    public string WarningTypeName { get; set; } = string.Empty;
    public string Message { get; set; } = string.Empty;
    public string? LocationLabel { get; set; }
}

public sealed class ProjectDashboardThreadItemViewModel
{
    public int PlotThreadID { get; set; }
    public string ThreadTitle { get; set; } = string.Empty;
    public string PlotLineName { get; set; } = string.Empty;
    public string ThreadStatusName { get; set; } = string.Empty;
    public int Importance { get; set; }
    public string AttentionLabel { get; set; } = string.Empty;
}

public sealed class ProjectDashboardOverdueBookViewModel
{
    public int BookID { get; set; }
    public string BookTitle { get; set; } = string.Empty;
    public DateTime TargetCompletionDate { get; set; }
}

public sealed class ProjectDashboardBookAttentionItemViewModel
{
    public int BookID { get; set; }
    public string BookTitle { get; set; } = string.Empty;
    public string Message { get; set; } = string.Empty;
    public string Label { get; set; } = "Worth a look";
}

public sealed class ProjectCollaboratorsViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<ProjectCollaborator> Collaborators { get; set; } = [];
    public IReadOnlyList<ProjectInvitation> PendingInvitations { get; set; } = [];
    public ProjectCollaboratorInviteViewModel Invite { get; set; } = new();
}

public sealed class ProjectCollaboratorInviteViewModel
{
    public int ProjectID { get; set; }

    [Required, EmailAddress, StringLength(256)]
    [Display(Name = "Collaborator email")]
    public string EmailAddress { get; set; } = string.Empty;
}

public sealed class ProjectActivityViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<ProjectActivity> Activities { get; set; } = [];
}

public sealed class BookEditViewModel
{
    public int BookID { get; set; }
    public int ProjectID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Book title")]
    public string BookTitle { get; set; } = string.Empty;

    [StringLength(200)]
    public string? Subtitle { get; set; }

    [StringLength(300)]
    public string? Tagline { get; set; }

    [Display(Name = "Short description")]
    public string? ShortDescription { get; set; }

    [Required, StringLength(50)]
    public string Status { get; set; } = BookStatuses.Planning;

    [Display(Name = "Book number")]
    public int BookNumber { get; set; } = 1;

    [Display(Name = "Target word count")]
    [Range(1, int.MaxValue, ErrorMessage = "Target word count must be greater than zero.")]
    public int? TargetWordCount { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Writing start date")]
    public DateTime? WritingStartDate { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Target completion date")]
    public DateTime? TargetCompletionDate { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Publication date")]
    public DateTime? PublicationDate { get; set; }

    [Display(Name = "Uses explicit scenes")]
    public bool UsesExplicitScenes { get; set; }

    [Display(Name = "Cover image")]
    public IFormFile? CoverUpload { get; set; }

    public string CoverThumbnailPath { get; set; } = "/images/placeholders/book-cover.svg";
    public string? CoverStandardPath { get; set; }

    public string? Description { get; set; }
    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> StatusOptions { get; set; } = [];
    public string BeginMode { get; set; } = BookBeginModes.Manual;
}

public static class BookBeginModes
{
    public const string Manual = "Manual";
    public const string Import = "Import";
}

public sealed class BookDetailViewModel
{
    public Project Project { get; set; } = new();
    public Book Book { get; set; } = new();
    public ManuscriptDocumentModel? ManuscriptDocument { get; set; }
    public IReadOnlyList<Chapter> Chapters { get; set; } = [];
    public StoryIntelligenceBookPipelineState? StoryIntelligencePipeline { get; set; }
}

public sealed class ChapterEditViewModel
{
    public int ChapterID { get; set; }
    public int BookID { get; set; }

    [Display(Name = "Chapter number")]
    public decimal ChapterNumber { get; set; } = 1;

    [Required, StringLength(200)]
    [Display(Name = "Chapter title")]
    public string ChapterTitle { get; set; } = string.Empty;

    public string? Summary { get; set; }

    [Display(Name = "Chapter purpose")]
    public int? ChapterPurposeID { get; set; }

    [Display(Name = "Revision status")]
    public int RevisionStatusID { get; set; }

    public bool IsLinkedToManuscript { get; set; }

    public Book? Book { get; set; }
    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> RevisionStatuses { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterPurposes { get; set; } = [];
}

public sealed class ChapterDetailViewModel
{
    public Project Project { get; set; } = new();
    public Book Book { get; set; } = new();
    public Chapter Chapter { get; set; } = new();
    public IReadOnlyList<Scene> Scenes { get; set; } = [];
    public int PendingCharacterSuggestionCount { get; set; }
    public int PendingAssetSuggestionCount { get; set; }
    public int PendingLocationSuggestionCount { get; set; }
}

public sealed class SceneEditViewModel
{
    public int SceneID { get; set; }
    public int ChapterID { get; set; }

    [Display(Name = "Scene number")]
    public decimal SceneNumber { get; set; } = 1;

    [Required, StringLength(200)]
    [Display(Name = "Scene title")]
    public string SceneTitle { get; set; } = string.Empty;

    public string? Summary { get; set; }

    [Display(Name = "POV character")]
    public int? POVCharacterID { get; set; }

    [Display(Name = "Time mode")]
    public int TimeModeID { get; set; }

    public string TimeModeName { get; set; } = string.Empty;

    [Display(Name = "Start date/time")]
    public DateTime? StartDateTime { get; set; }

    [Display(Name = "End date/time")]
    public DateTime? EndDateTime { get; set; }

    [Display(Name = "Duration")]
    public decimal? DurationAmount { get; set; }

    [Display(Name = "Duration unit")]
    public int? DurationUnitID { get; set; }

    [Display(Name = "Relative time")]
    public string? RelativeTimeText { get; set; }

    [Display(Name = "Time confidence")]
    public int TimeConfidenceID { get; set; }

    [Display(Name = "Purpose notes")]
    public string? ScenePurposeNotes { get; set; }

    [Display(Name = "Outcome notes")]
    public string? SceneOutcomeNotes { get; set; }

    [Display(Name = "Revision status")]
    public int RevisionStatusID { get; set; }

    public bool IsLinkedToManuscript { get; set; }

    [Display(Name = "Primary location")]
    public int? PrimaryLocationID { get; set; }

    [Display(Name = "Floor plan")]
    public int? FloorPlanID { get; set; }

    [Display(Name = "Initial floor")]
    public int? InitialFloorPlanFloorID { get; set; }

    [Display(Name = "Scene purposes")]
    public List<int> SelectedPurposeTypeIds { get; set; } = [];

    public List<SceneMetricInputViewModel> Metrics { get; set; } = [];
    public IReadOnlyList<ThreadEvent> ThreadEvents { get; set; } = [];
    public ThreadEventCreateViewModel NewThreadEvent { get; set; } = new();
    public IReadOnlyList<AssetEvent> AssetEvents { get; set; } = [];
    public IReadOnlyList<AssetCustodyEvent> AssetCustodyEvents { get; set; } = [];
    public IReadOnlyList<SceneAssetLocation> SceneAssetLocations { get; set; } = [];
    public AssetEventCreateViewModel NewAssetEvent { get; set; } = new();
    public AssetCustodyCreateViewModel NewAssetCustodyEvent { get; set; } = new();
    public SceneAssetLocationCreateViewModel NewSceneAssetLocation { get; set; } = new();
    public IReadOnlyList<SceneCharacter> SceneCharacters { get; set; } = [];
    public IReadOnlyList<SceneCharacterSuggestion> CharacterSuggestions { get; set; } = [];
    public IReadOnlyList<SceneAssetSuggestion> AssetSuggestions { get; set; } = [];
    public IReadOnlyList<SceneLocationSuggestion> LocationSuggestions { get; set; } = [];
    public IReadOnlyList<CharacterAttributeEvent> CharacterAttributeEvents { get; set; } = [];
    public IReadOnlyList<CharacterKnowledgeItem> CharacterKnowledge { get; set; } = [];
    public IReadOnlyList<RelationshipEvent> RelationshipEvents { get; set; } = [];
    public SceneCharacterCreateViewModel NewSceneCharacter { get; set; } = new();
    public CharacterAttributeEventCreateViewModel NewCharacterAttributeEvent { get; set; } = new();
    public CharacterKnowledgeCreateViewModel NewCharacterKnowledge { get; set; } = new();
    public RelationshipEventCreateViewModel NewRelationshipEvent { get; set; } = new();
    public IReadOnlyList<SelectListItem> PlotLineOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PlotThreadOptions { get; set; } = [];
    public IReadOnlyList<PlotLineItem> PlotLineEventOptions { get; set; } = [];
    public IReadOnlyList<PlotThread> PlotThreadEventOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ThreadTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ThreadEventTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PlotEventTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetKindOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetEventTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetCustodyEventTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CustodyRoleOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterRoleOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PresenceTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterAttributeTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> KnowledgeStateOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipStateOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneDependencyTypeOptions { get; set; } = [];
    public IReadOnlyList<string> AssetDependencyMessages { get; set; } = [];
    public IReadOnlyList<string> LocationConsistencyMessages { get; set; } = [];
    public IReadOnlyList<ContinuityWarning> Warnings { get; set; } = [];
    public IReadOnlyList<ContinuityWarning> AgeContinuityWarnings { get; set; } = [];
    public IReadOnlyList<SceneDependency> DependenciesThisSceneNeeds { get; set; } = [];
    public IReadOnlyList<SceneDependency> DependenciesNeedingThisScene { get; set; } = [];
    public SceneDependencyCreateViewModel NewDependency { get; set; } = new();
    public SceneMoveRequestViewModel MoveRequest { get; set; } = new();
    public SceneWorkflow Workflow { get; set; } = new();
    public IReadOnlyList<SceneNote> Notes { get; set; } = [];
    public IReadOnlyList<SceneChecklistItem> ChecklistItems { get; set; } = [];
    public IReadOnlyList<SceneAttachment> Attachments { get; set; } = [];
    public SceneNoteEditViewModel NewNote { get; set; } = new();
    public SceneChecklistItemEditViewModel NewChecklistItem { get; set; } = new();
    public SceneAttachmentEditViewModel NewAttachment { get; set; } = new();
    public IReadOnlyList<SelectListItem> SceneNoteTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneAttachmentTypeOptions { get; set; } = [];
    public StorageUsage? StorageUsage { get; set; }
    public StoryStateViewModel StoryState { get; set; } = new();
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public bool ReturnToTimeline { get; set; }

    public Chapter? Chapter { get; set; }
    public Book? Book { get; set; }
    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> RevisionStatuses { get; set; } = [];
    public IReadOnlyList<SelectListItem> TimeModes { get; set; } = [];
    public IReadOnlyList<SelectListItem> DurationUnits { get; set; } = [];
    public IReadOnlyList<SelectListItem> TimeConfidences { get; set; } = [];
    public IReadOnlyList<ScenePurposeType> ScenePurposeTypes { get; set; } = [];
}

public sealed class SceneDetailsSaveViewModel
{
    public int SceneID { get; set; }

    [Display(Name = "Scene number")]
    public decimal SceneNumber { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Scene title")]
    public string SceneTitle { get; set; } = string.Empty;

    [Display(Name = "Revision status")]
    public int RevisionStatusID { get; set; }

    [Display(Name = "POV character")]
    public int? POVCharacterID { get; set; }

    [Display(Name = "Primary location")]
    public int? PrimaryLocationID { get; set; }

    [Display(Name = "Synopsis")]
    public string? Summary { get; set; }

    [Display(Name = "Time mode")]
    public int TimeModeID { get; set; }

    [Display(Name = "Time confidence")]
    public int TimeConfidenceID { get; set; }

    [Display(Name = "Start date/time")]
    public DateTime? StartDateTime { get; set; }

    [Display(Name = "End date/time")]
    public DateTime? EndDateTime { get; set; }

    [Display(Name = "Duration")]
    public decimal? DurationAmount { get; set; }

    [Display(Name = "Duration unit")]
    public int? DurationUnitID { get; set; }

    [Display(Name = "Relative time")]
    public string? RelativeTimeText { get; set; }
}

public sealed class ScenePurposeSaveViewModel
{
    public int SceneID { get; set; }
    public List<int> SelectedPurposeTypeIds { get; set; } = [];

    [Display(Name = "Purpose notes")]
    public string? ScenePurposeNotes { get; set; }

    [Display(Name = "Outcome notes")]
    public string? SceneOutcomeNotes { get; set; }
}

public sealed class SceneMetricsSaveViewModel
{
    public int SceneID { get; set; }
    public List<SceneMetricInputViewModel> Metrics { get; set; } = [];
}

public sealed class SceneCharactersSaveViewModel
{
    public int SceneID { get; set; }
    public int? POVCharacterID { get; set; }
    public List<SceneCharacterParticipationInputViewModel> Characters { get; set; } = [];
    public SceneCharacterCreateViewModel NewCharacter { get; set; } = new();
}

public sealed class SceneLocationsSaveViewModel
{
    public int SceneID { get; set; }
    public int? PrimaryLocationID { get; set; }
}

public sealed class SceneAssetsSaveViewModel
{
    public int SceneID { get; set; }
    public List<SceneAssetEventInputViewModel> AssetEvents { get; set; } = [];
    public AssetEventCreateViewModel NewAssetEvent { get; set; } = new();
}

public sealed class SceneAssetEventInputViewModel
{
    public int AssetEventID { get; set; }
    public int StoryAssetID { get; set; }
    public bool Remove { get; set; }
}

public sealed class SceneNotesSaveViewModel
{
    public int SceneID { get; set; }
    public List<SceneNoteInputViewModel> Notes { get; set; } = [];
    public SceneNoteEditViewModel NewNote { get; set; } = new();
}

public sealed class SceneNoteInputViewModel
{
    public int SceneNoteID { get; set; }
    public int SceneID { get; set; }
    public int SceneNoteTypeID { get; set; }
    public string? NoteTitle { get; set; }
    public string NoteText { get; set; } = string.Empty;
    public int? SortOrder { get; set; }
    public bool IsPinned { get; set; }
    public bool IsResolved { get; set; }
    public bool Remove { get; set; }
}

public sealed class SceneChecklistSaveViewModel
{
    public int SceneID { get; set; }
    public List<SceneChecklistItemInputViewModel> ChecklistItems { get; set; } = [];
    public SceneChecklistItemEditViewModel NewChecklistItem { get; set; } = new();
}

public sealed class SceneChecklistItemInputViewModel
{
    public int SceneChecklistItemID { get; set; }
    public int SceneID { get; set; }
    public string ItemText { get; set; } = string.Empty;
    public bool IsCompleted { get; set; }
    public int? SortOrder { get; set; }
    public bool Remove { get; set; }
}

public sealed class SceneCharacterParticipationInputViewModel
{
    public int SceneCharacterID { get; set; }
    public int SceneID { get; set; }
    public int CharacterID { get; set; }
    public bool Remove { get; set; }
    public int? RoleInSceneTypeID { get; set; }
    public int? PresenceTypeID { get; set; }
    public int? LocationID { get; set; }
    public int? EntryLocationID { get; set; }
    public int? ExitLocationID { get; set; }
    public string? AppearanceNotes { get; set; }
    public string? OutfitDescription { get; set; }
    public string? PhysicalCondition { get; set; }
    public string? EmotionalState { get; set; }
}

public sealed class StoryStateViewModel
{
    public List<CharacterStoryStateViewModel> Characters { get; set; } = [];
    public List<AssetStoryStateViewModel> Assets { get; set; } = [];
    public List<LocationStoryStateViewModel> Locations { get; set; } = [];
}

public sealed class CharacterStoryStateViewModel
{
    public int CharacterID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public int? LocationID { get; set; }
    public string CurrentLocation { get; set; } = "Unknown";
    public string State { get; set; } = "Unknown";
    public int? LastConfirmedSceneID { get; set; }
    public string LastConfirmed { get; set; } = "Never";
}

public sealed class AssetStoryStateViewModel
{
    public int StoryAssetID { get; set; }
    public string AssetName { get; set; } = string.Empty;
    public int? LocationID { get; set; }
    public string CurrentHolderOrLocation { get; set; } = "Unknown";
    public string State { get; set; } = "Unknown";
    public int? LastConfirmedSceneID { get; set; }
    public string LastConfirmed { get; set; } = "Never";
}

public sealed class LocationStoryStateViewModel
{
    public int LocationID { get; set; }
    public string LocationName { get; set; } = string.Empty;
    public string LocationPath { get; set; } = string.Empty;
    public List<string> CharactersPresent { get; set; } = [];
    public List<string> AssetsPresent { get; set; } = [];
}

public sealed class ContinuityFilter
{
    public int ProjectID { get; set; }
    public List<string> EntityTypes { get; set; } = ["Characters", "Assets", "Locations"];
    public List<int> CharacterIDs { get; set; } = [];
    public List<int> AssetIDs { get; set; } = [];
    public List<int> LocationIDs { get; set; } = [];
    public string SceneRangeMode { get; set; } = "EntireProject";
    public int? BookID { get; set; }
    public int? ChapterID { get; set; }
    public int? StartSceneID { get; set; }
    public int? EndSceneID { get; set; }
    public string DisplayMode { get; set; } = "StoryState";
    public int? SelectedSceneID { get; set; }
    public bool ShowAcknowledgedWarnings { get; set; }

    public bool IncludeCharacters => EntityTypes.Contains("Characters", StringComparer.OrdinalIgnoreCase);
    public bool IncludeAssets => EntityTypes.Contains("Assets", StringComparer.OrdinalIgnoreCase);
    public bool IncludeLocations => EntityTypes.Contains("Locations", StringComparer.OrdinalIgnoreCase);
}

public sealed class ContinuityExplorerViewModel
{
    public Project Project { get; set; } = new();
    public ContinuityFilter Filter { get; set; } = new();
    public StoryStateViewModel StoryState { get; set; } = new();
    public IReadOnlyList<CharacterJourneyViewModel> CharacterJourney { get; set; } = [];
    public IReadOnlyList<AssetJourneyViewModel> AssetJourney { get; set; } = [];
    public IReadOnlyList<LocationActivityViewModel> LocationActivity { get; set; } = [];
    public IReadOnlyList<ContinuityWarningViewModel> ContinuityWarnings { get; set; } = [];
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
    public bool ShowBookContext { get; set; }
    public bool ShowChapterContext { get; set; }
}

public sealed class ContinuityWarningViewModel
{
    public string WarningKey { get; set; } = string.Empty;
    public string WarningType { get; set; } = string.Empty;
    public string WarningTypeDisplayName { get; set; } = string.Empty;
    public string Severity { get; set; } = "Information";
    public string Message { get; set; } = string.Empty;
    public int? CharacterID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public int? AssetID { get; set; }
    public int? BookID { get; set; }
    public int? ChapterID { get; set; }
    public int? SceneID { get; set; }
    public string SceneDisplayName { get; set; } = string.Empty;
    public string ContextSceneDisplayName { get; set; } = string.Empty;
    public string BookDisplayName { get; set; } = string.Empty;
    public string ChapterDisplayName { get; set; } = string.Empty;
    public bool IsAcknowledged { get; set; }
    public string? AcknowledgementNotes { get; set; }
    public int? KnowledgeId { get; set; }
    public string KnowledgeTitle { get; set; } = string.Empty;
    public string KnowledgeDescription { get; set; } = string.Empty;
    public string PreviousKnowledgeState { get; set; } = string.Empty;
    public string CurrentKnowledgeState { get; set; } = string.Empty;
    public int? PreviousBookId { get; set; }
    public int? PreviousChapterId { get; set; }
    public int? PreviousSceneId { get; set; }
    public string PreviousSceneDisplayName { get; set; } = string.Empty;
    public string PreviousContextSceneDisplayName { get; set; } = string.Empty;
    public int? CurrentBookId { get; set; }
    public int? CurrentChapterId { get; set; }
    public int? CurrentSceneId { get; set; }
    public string CurrentSceneDisplayName { get; set; } = string.Empty;
    public string CurrentContextSceneDisplayName { get; set; } = string.Empty;
    public int? EstablishedSceneId { get; set; }
    public string EstablishedSceneDisplayName { get; set; } = string.Empty;
    public string EstablishedContextSceneDisplayName { get; set; } = string.Empty;
    public int? ReferencedSceneId { get; set; }
    public string ReferencedSceneDisplayName { get; set; } = string.Empty;
    public string ReferencedContextSceneDisplayName { get; set; } = string.Empty;
    public string InvestigationDisplayMode { get; set; } = string.Empty;
    public int? InvestigationSceneId { get; set; }
    public bool CanInvestigate { get; set; }
}

public sealed class CharacterJourneyViewModel
{
    public int CharacterID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public int BookID { get; set; }
    public string BookDisplayName { get; set; } = string.Empty;
    public int ChapterID { get; set; }
    public string ChapterDisplayName { get; set; } = string.Empty;
    public int SceneID { get; set; }
    public string SceneTitle { get; set; } = string.Empty;
    public string SceneLabel { get; set; } = string.Empty;
    public string SceneDisplayName { get; set; } = string.Empty;
    public string Location { get; set; } = string.Empty;
}

public sealed class AssetJourneyViewModel
{
    public int StoryAssetID { get; set; }
    public string AssetName { get; set; } = string.Empty;
    public int BookID { get; set; }
    public string BookDisplayName { get; set; } = string.Empty;
    public int ChapterID { get; set; }
    public string ChapterDisplayName { get; set; } = string.Empty;
    public int SceneID { get; set; }
    public string SceneTitle { get; set; } = string.Empty;
    public string SceneLabel { get; set; } = string.Empty;
    public string SceneDisplayName { get; set; } = string.Empty;
    public string HolderOrLocation { get; set; } = string.Empty;
}

public sealed class LocationActivityViewModel
{
    public int LocationID { get; set; }
    public string LocationName { get; set; } = string.Empty;
    public int BookID { get; set; }
    public string BookDisplayName { get; set; } = string.Empty;
    public int ChapterID { get; set; }
    public string ChapterDisplayName { get; set; } = string.Empty;
    public int SceneID { get; set; }
    public string SceneTitle { get; set; } = string.Empty;
    public string SceneLabel { get; set; } = string.Empty;
    public string SceneDisplayName { get; set; } = string.Empty;
    public string CharactersPresent { get; set; } = string.Empty;
    public string AssetsPresent { get; set; } = string.Empty;
    public List<LocationActivityEntityViewModel> Characters { get; set; } = [];
    public List<LocationActivityEntityViewModel> Assets { get; set; } = [];
}

public sealed class LocationActivityEntityViewModel
{
    public int EntityID { get; set; }
    public string EntityName { get; set; } = string.Empty;
    public string EntityType { get; set; } = string.Empty;
    public string StateType { get; set; } = string.Empty;
    public int? LastConfirmedSceneID { get; set; }
    public string LastConfirmedSceneDisplayName { get; set; } = string.Empty;
    public string? Note { get; set; }
}

public sealed class SceneWorkflowEditViewModel
{
    public int SceneID { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public string DraftStatus { get; set; } = "Planned";
    public int? EstimatedWordCount { get; set; }
    public int? ActualWordCount { get; set; }
    public DateTime? DraftedDate { get; set; }
    public DateTime? LastWorkedOn { get; set; }
    public bool ReadyForDraft { get; set; }
    public bool ReadyForRevision { get; set; }
    public bool ReadyForPolish { get; set; }
    public bool IsBlocked { get; set; }
    public string? BlockedReason { get; set; }
    public int? Priority { get; set; }
}

public sealed class SceneNoteEditViewModel
{
    public int SceneNoteID { get; set; }
    public int SceneID { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public int SceneNoteTypeID { get; set; }
    public string? NoteTitle { get; set; }
    public string NoteText { get; set; } = string.Empty;
    public int? SortOrder { get; set; }
    public bool IsPinned { get; set; }
    public bool IsResolved { get; set; }
}

public sealed class SceneChecklistItemEditViewModel
{
    public int SceneChecklistItemID { get; set; }
    public int SceneID { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public string ItemText { get; set; } = string.Empty;
    public bool IsCompleted { get; set; }
    public int? SortOrder { get; set; }
}

public sealed class SceneAttachmentEditViewModel
{
    public int SceneAttachmentID { get; set; }
    public int SceneID { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public int SceneAttachmentTypeID { get; set; }
    public string Title { get; set; } = string.Empty;
    public string? FilePath { get; set; }
    public string? ExternalUrl { get; set; }
    public string? Notes { get; set; }
    public IFormFile? UploadedFile { get; set; }
}

public sealed class ChapterGoalEditViewModel
{
    public int ChapterGoalID { get; set; }
    public int ChapterID { get; set; }
    public string? GoalSummary { get; set; }
    public string? EmotionalGoal { get; set; }
    public string? ReaderTakeaway { get; set; }
    public string? MustInclude { get; set; }
    public string? RisksOrConcerns { get; set; }
}

public sealed class ChapterWorkflowViewModel
{
    public Project Project { get; set; } = new();
    public Book Book { get; set; } = new();
    public Chapter Chapter { get; set; } = new();
    public ChapterGoal Goal { get; set; } = new();
    public IReadOnlyList<ChapterWorkflowScene> Scenes { get; set; } = [];
}

public sealed class WriterDashboardViewModel
{
    public int? ProjectID { get; set; }
    public IReadOnlyList<SelectListItem> ProjectOptions { get; set; } = [];
    public WritingScheduleDashboardViewModel WritingSchedule { get; set; } = new();
    public IReadOnlyList<WriterDashboardScene> WriteNext { get; set; } = [];
    public IReadOnlyList<WriterDashboardScene> ContinueWriting { get; set; } = [];
    public IReadOnlyList<WriterDashboardScene> RevisionQueue { get; set; } = [];
    public IReadOnlyList<WriterDashboardScene> PolishingQueue { get; set; } = [];
    public IReadOnlyList<StoryBibleAttentionItem> StoryHealthReminders { get; set; } = [];
}

public sealed class WritingScheduleDashboardViewModel
{
    public int ActivePlanCount { get; set; }
    public int RebalancePlanCount { get; set; }
    public IReadOnlyList<WritingScheduleDashboardItem> TodayTasks { get; set; } = [];
    public IReadOnlyList<WritingScheduleDashboardItem> UpcomingTasks { get; set; } = [];
    public int TotalPlannedTasks { get; set; }
    public int CompletedTasks { get; set; }
    public int SkippedTasks { get; set; }
    public int RemainingTasks { get; set; }
    public DateTime? ProjectedFinishDate { get; set; }
    public bool HasActivePlans => ActivePlanCount > 0;
    public bool HasActiveSchedule => TotalPlannedTasks > 0;
}

public sealed class WritingPlanViewModel
{
    public int WritingPlanID { get; set; }
    public int UserID { get; set; }
    public int ProjectID { get; set; }
    public int? BookID { get; set; }
    public string PlanName { get; set; } = string.Empty;
    public string GoalType { get; set; } = string.Empty;
    public DateTime StartDate { get; set; }
    public DateTime DeadlineDate { get; set; }
    public string AvailableDaysJson { get; set; } = string.Empty;
    public int SessionLengthMinutes { get; set; }
    public bool IncludeBlockedScenes { get; set; }
    public string RebalanceMode { get; set; } = "Ask";
    public bool IsActive { get; set; } = true;
    public DateTime CreatedDateUtc { get; set; }
    public DateTime ModifiedDateUtc { get; set; }
}

public sealed class WritingPlansManagementViewModel
{
    public IReadOnlyList<WritingPlanManagementItem> Plans { get; set; } = [];
}

public sealed class WritingDayAvailabilityViewModel
{
    public string Day { get; set; } = string.Empty;
    public bool IsAvailable { get; set; }
    public int Minutes { get; set; } = 60;
}

public sealed class WritingPlanSettingsEditViewModel
{
    public int WritingPlanID { get; set; }

    [Required, StringLength(200)]
    public string PlanName { get; set; } = string.Empty;

    public string ProjectName { get; set; } = string.Empty;
    public string BookName { get; set; } = "All Books";
    public string GoalType { get; set; } = string.Empty;

    [DataType(DataType.Date)]
    public DateTime StartDate { get; set; }

    [DataType(DataType.Date)]
    public DateTime DeadlineDate { get; set; }

    [Range(1, int.MaxValue, ErrorMessage = "Session length minutes must be greater than zero.")]
    public int SessionLengthMinutes { get; set; }

    public List<string> SelectedWritingDays { get; set; } = [];
    public List<WritingDayAvailabilityViewModel> WritingDayAvailability { get; set; } = [];
    public bool IncludeBlockedScenes { get; set; }
    public string RebalanceMode { get; set; } = "Ask";
    public bool IsActive { get; set; }
    public IReadOnlyList<SelectListItem> RebalanceModeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SessionLengthOptions { get; set; } = [];
    public IReadOnlyList<string> WritingDayOptions { get; set; } =
    [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
    ];
}

public sealed class WritingScheduleItemViewModel
{
    public int WritingScheduleItemID { get; set; }
    public int WritingPlanID { get; set; }
    public int SceneID { get; set; }
    public DateTime ScheduledDate { get; set; }
    public string TaskType { get; set; } = string.Empty;
    public int? EstimatedMinutes { get; set; }
    public int? TargetWords { get; set; }
    public string ScheduleStatus { get; set; } = string.Empty;
    public DateTime? CompletedDateUtc { get; set; }
    public string? Notes { get; set; }
    public DateTime CreatedDateUtc { get; set; }
    public DateTime ModifiedDateUtc { get; set; }
}

public sealed class WritingSchedulePreviewViewModel
{
    public int? SelectedWritingPlanID { get; set; }
    public IReadOnlyList<WritingPlanSummary> Plans { get; set; } = [];
    public IReadOnlyList<WritingPlanSummary> PlansRequiringRebalance { get; set; } = [];
    public WritingPlanSummary? SelectedPlan { get; set; }
    public IReadOnlyList<WritingSchedulePreviewItem> Items { get; set; } = [];
    public WritingScheduleRebalancePreviewViewModel? RebalancePreview { get; set; }
    public bool SelectedPlanRequiresRebalance => SelectedWritingPlanID.HasValue
        && PlansRequiringRebalance.Any(x => x.WritingPlanID == SelectedWritingPlanID.Value);
    public int TotalTasks => Items.Count;
    public int DraftTasks => Items.Count(x => x.TaskType == "Draft");
    public int RevisionTasks => Items.Count(x => x.TaskType == "Revise");
    public int PolishTasks => Items.Count(x => x.TaskType == "Polish");
    public int TotalEstimatedMinutes => Items.Sum(x => x.EstimatedMinutes ?? 0);
    public DateTime? ProjectedFinishDate => Items.Count == 0 ? null : Items.Max(x => x.ScheduledDate);
}

public sealed class WritingScheduleRebalancePreviewViewModel
{
    public int WritingPlanID { get; set; }
    public DateTime? OriginalFinishDate { get; set; }
    public DateTime? ProposedFinishDate { get; set; }
    public IReadOnlyList<WritingScheduleRebalanceChangeViewModel> Changes { get; set; } = [];
    public int TasksAffected => Changes.Count;
}

public sealed class WritingScheduleRebalanceChangeViewModel
{
    public int WritingScheduleItemID { get; set; }
    public string TaskType { get; set; } = string.Empty;
    public string SceneName { get; set; } = string.Empty;
    public DateTime CurrentDate { get; set; }
    public DateTime ProposedDate { get; set; }
}

public sealed class WritingScheduleSetupViewModel
{
    [Required, StringLength(200)]
    public string PlanName { get; set; } = "Writing Schedule";

    [Required]
    public int? ProjectID { get; set; }

    public int? BookID { get; set; }

    [Required]
    public string GoalType { get; set; } = "FirstDraft";

    [DataType(DataType.Date)]
    public DateTime StartDate { get; set; } = DateTime.Today;

    [DataType(DataType.Date)]
    public DateTime DeadlineDate { get; set; } = DateTime.Today.AddMonths(1);

    public List<string> SelectedWritingDays { get; set; } = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

    [Range(1, int.MaxValue, ErrorMessage = "Session length minutes must be greater than zero.")]
    public int SessionLengthMinutes { get; set; } = 60;
    public List<WritingDayAvailabilityViewModel> WritingDayAvailability { get; set; } = [];

    public bool IncludeBlockedScenes { get; set; }

    [Required]
    public string RebalanceMode { get; set; } = "Ask";

    public IReadOnlyList<SelectListItem> ProjectOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> GoalTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RebalanceModeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SessionLengthOptions { get; set; } = [];
    public IReadOnlyList<string> WritingDayOptions { get; set; } =
    [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
    ];
}

public sealed class WritingScheduleSetupResult
{
    public int WritingPlanID { get; init; }
    public int GeneratedItemCount { get; init; }
    public string? ValidationMessage { get; init; }
}

public sealed class CharacterArcViewModel
{
    public Project Project { get; set; } = new();
    public Character Character { get; set; } = new();
    public IReadOnlyList<CharacterArcPoint> ArcPoints { get; set; } = [];
    public IReadOnlyList<RelationshipEvent> RelationshipEvents { get; set; } = [];
    public IReadOnlyList<CharacterKnowledgeItem> KnowledgeEvents { get; set; } = [];
    public IReadOnlyList<DynamicsIssue> Issues { get; set; } = [];
}

public sealed class RelationshipEvolutionViewModel
{
    public Project Project { get; set; } = new();
    public CharacterRelationship Relationship { get; set; } = new();
    public IReadOnlyList<RelationshipProgressionPoint> Events { get; set; } = [];
    public IReadOnlyList<DynamicsIssue> Issues { get; set; } = [];
}

public sealed class KnowledgeTimelineViewModel
{
    public Project Project { get; set; } = new();
    public Character Character { get; set; } = new();
    public IReadOnlyList<CharacterKnowledgeItem> KnowledgeEvents { get; set; } = [];
    public IReadOnlyList<DynamicsIssue> Issues { get; set; } = [];
}

public sealed class StoryDynamicsViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<CharacterPairingSummary> Pairings { get; set; } = [];
    public IReadOnlyList<DynamicsIssue> Issues { get; set; } = [];
}

public sealed class ScenarioListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<Scenario> Scenarios { get; set; } = [];
}

public sealed class ScenarioCreateViewModel
{
    public int ProjectID { get; set; }
    public int? BookID { get; set; }
    public string ScenarioName { get; set; } = string.Empty;
    public string? Description { get; set; }
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
}

public sealed class ScenarioTimelineViewModel
{
    public Project Project { get; set; } = new();
    public Scenario Scenario { get; set; } = new();
    public IReadOnlyList<Book> Books { get; set; } = [];
    public IReadOnlyList<Chapter> Chapters { get; set; } = [];
    public IReadOnlyList<ScenarioScene> Scenes { get; set; } = [];
    public IReadOnlyList<ScenarioWarning> Warnings { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
}

public sealed class ScenarioCompareViewModel
{
    public Project Project { get; set; } = new();
    public Scenario Scenario { get; set; } = new();
    public IReadOnlyList<ScenarioComparisonRow> MovedScenes { get; set; } = [];
    public IReadOnlyList<ScenarioWarning> ScenarioWarnings { get; set; } = [];
}

public sealed class ArchivedItemsViewModel
{
    public int? ProjectID { get; set; }
    public string? EntityType { get; set; }
    public IReadOnlyList<SelectListItem> ProjectOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> EntityTypeOptions { get; set; } = [];
    public IReadOnlyList<ArchivedItem> Items { get; set; } = [];
}

public sealed class Phase1AcceptanceViewModel
{
    public int? ProjectID { get; set; }
    public string? ProjectName { get; set; }
    public IReadOnlyList<SelectListItem> ProjectOptions { get; set; } = [];
    public IReadOnlyList<Phase1AcceptanceChecklistItem> Items { get; set; } = [];
    public int PassCount => Items.Count(x => x.Status == "Pass");
    public int ReviewCount => Items.Count(x => x.Status == "Review");
    public int BlockedCount => Items.Count(x => x.Status == "Blocked");
}

public sealed class TimelineViewModel
{
    public Project Project { get; set; } = new();
    public ProjectTimelineSettings Settings { get; set; } = new();
    public int? SelectedBookID { get; set; }
    public int? SelectedSceneID { get; set; }
    public SceneEditViewModel? SelectedScene { get; set; }
    public TimelineFilterViewModel Filter { get; set; } = new();
    public IReadOnlyList<BookTimelineViewModel> Books { get; set; } = [];
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterFromOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterToOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneFromOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneToOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PlotLineOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PlotThreadOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetOptions { get; set; } = [];
    public IReadOnlyList<StoryAsset> QuickAddAssets { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetEventTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetDependencyTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterOptions { get; set; } = [];
    public IReadOnlyList<Character> QuickAddCharacters { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterRoleOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PresenceTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> PovCharacterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
    public IReadOnlyList<LocationItem> QuickAddLocations { get; set; } = [];
    public IReadOnlyList<SelectListItem> LocationRelationshipTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> WarningSeverityOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> WarningTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RevisionStatusOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ScenePurposeOptions { get; set; } = [];
    public IReadOnlyList<TimelineViewPreset> ViewPresets { get; set; } = [];
    public IReadOnlyList<Scene> OrderedScenes { get; set; } = [];
    public IReadOnlyList<MetricGraphViewModel> MetricGraphs { get; set; } = [];
    public IReadOnlyList<PlotLineLaneViewModel> PlotLanes { get; set; } = [];
    public IReadOnlyList<AssetLaneViewModel> AssetLanes { get; set; } = [];
    public IReadOnlyList<CharacterLaneViewModel> CharacterLanes { get; set; } = [];
    public IReadOnlyDictionary<int, TimelineSceneOverviewViewModel> SceneOverviews { get; set; } = new Dictionary<int, TimelineSceneOverviewViewModel>();
    public IReadOnlyDictionary<int, IReadOnlyList<ContinuityWarning>> WarningsByScene { get; set; } = new Dictionary<int, IReadOnlyList<ContinuityWarning>>();
    public IReadOnlySet<int> MatchingSceneIDs { get; set; } = new HashSet<int>();
    public IReadOnlySet<int> FocusSceneIDs { get; set; } = new HashSet<int>();
    public IReadOnlyDictionary<int, string> ScenePovLabels { get; set; } = new Dictionary<int, string>();
    public string? FocusLabel { get; set; }
    public ContinuityValidationSummary? LastValidationSummary { get; set; }
}

public sealed class TimelineSettingsViewModel
{
    public int ProjectID { get; set; }
    public string ProjectName { get; set; } = string.Empty;
    public bool ShowSceneCards { get; set; } = true;
    public bool ShowMetricShape { get; set; } = true;
    public bool ShowPlotLines { get; set; } = true;
    public bool ShowStoryAssets { get; set; }
    public bool ShowCharacterAppearances { get; set; }
    public bool ShowWarnings { get; set; } = true;
    public List<int> SelectedMetricTypeIDs { get; set; } = [];
    public IReadOnlyList<TimelineMetricSettingOptionViewModel> MetricOptions { get; set; } = [];
}

public sealed class TimelineMetricSettingOptionViewModel
{
    public int MetricTypeID { get; set; }
    public string MetricName { get; set; } = string.Empty;
    public string? Description { get; set; }
    [Display(Name = "Level")]
    [Range(-10, 50)]
    public int SortOrder { get; set; }
    public bool IsSelected { get; set; }
}

public sealed class TimelineSceneOverviewViewModel
{
    public IReadOnlyList<TimelineSceneCharacterOverviewViewModel> Characters { get; set; } = [];
    public IReadOnlyList<TimelineSceneAssetOverviewViewModel> Assets { get; set; } = [];
    public IReadOnlyList<string> Locations { get; set; } = [];
}

public sealed class TimelineSceneCharacterOverviewViewModel
{
    public int CharacterID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public bool IsPov { get; set; }
}

public sealed class TimelineSceneAssetOverviewViewModel
{
    public int StoryAssetID { get; set; }
    public string AssetName { get; set; } = string.Empty;
    public int Importance { get; set; }
    public string? EventTypeName { get; set; }
}

public sealed class ProjectSceneMetricsViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<SceneMetricType> Metrics { get; set; } = [];
    public SceneMetricTypeEditViewModel NewMetric { get; set; } = new();
    public bool HasEnabledMetrics => Metrics.Any(x => x.IsEnabledForProject && x.IsActive);
}

public sealed class ProjectRestorePointListViewModel
{
    public Project Project { get; init; } = new();
    public IReadOnlyList<ProjectRestorePoint> RestorePoints { get; init; } = [];
}

public sealed class SceneMetricTypeEditViewModel
{
    public int MetricTypeID { get; set; }
    public int ProjectID { get; set; }

    [Required, StringLength(100)]
    [Display(Name = "Metric name")]
    public string MetricName { get; set; } = string.Empty;

    public string? Description { get; set; }

    [Display(Name = "Minimum value")]
    public int MinValue { get; set; } = 1;

    [Display(Name = "Maximum value")]
    public int MaxValue { get; set; } = 10;

    [Display(Name = "Default value")]
    public int DefaultValue { get; set; } = 5;

    [Display(Name = "Sort order")]
    public int SortOrder { get; set; } = 100;

    [Display(Name = "Active")]
    public bool IsActive { get; set; } = true;
}

public sealed class StoryBibleViewModel
{
    public Project Project { get; set; } = new();
    public string? Query { get; set; }
    public IReadOnlyList<StoryBibleCharacterSummary> Characters { get; set; } = [];
    public IReadOnlyList<StoryBibleThreadSummary> PlotThreads { get; set; } = [];
    public IReadOnlyList<StoryBibleAssetSummary> Assets { get; set; } = [];
    public IReadOnlyList<StoryBibleRelationshipSummary> Relationships { get; set; } = [];
    public IReadOnlyList<StoryBibleLocationSummary> Locations { get; set; } = [];
    public IReadOnlyList<StoryBibleTimelineSummary> Timeline { get; set; } = [];
    public IReadOnlyList<StoryBibleAttentionItem> OpenQuestions { get; set; } = [];
    public IReadOnlyList<StoryBibleRevisionItem> RevisionItems { get; set; } = [];
    public IReadOnlyList<StoryBibleSearchResult> SearchResults { get; set; } = [];
}

public sealed class TimelineFilterViewModel
{
    public int ProjectID { get; set; }
    public int? BookID { get; set; }
    public int? SelectedSceneID { get; set; }
    public int? ChapterFromID { get; set; }
    public int? ChapterToID { get; set; }
    public int? SceneFromID { get; set; }
    public int? SceneToID { get; set; }
    public int? PlotLineID { get; set; }
    public int? PlotThreadID { get; set; }
    public int? StoryAssetID { get; set; }
    public int? CharacterID { get; set; }
    public int? LocationID { get; set; }
    public int? WarningSeverityID { get; set; }
    public int? WarningTypeID { get; set; }
    public int? RevisionStatusID { get; set; }
    public int? ScenePurposeTypeID { get; set; }
    public int? POVCharacterID { get; set; }
    public string? FocusType { get; set; }
    public int? FocusID { get; set; }
    public bool ShowSceneCards { get; set; } = true;
    public bool ShowPlotLines { get; set; } = true;
    public bool ShowAssets { get; set; } = true;
    public bool ShowCharacters { get; set; } = true;
    public bool ShowLocations { get; set; } = true;
    public bool ShowWarnings { get; set; } = true;
    public bool ShowMetrics { get; set; } = true;
    public bool ShowWarningsOnly { get; set; }
    public bool IncludeDismissedWarnings { get; set; }
    public bool IncludeIntentionalWarnings { get; set; }
}

public sealed class TimelinePresetSaveViewModel
{
    public int ProjectID { get; set; }
    public int? BookID { get; set; }
    public string PresetName { get; set; } = string.Empty;
    public TimelineFilterViewModel Filter { get; set; } = new();
    public string VisibilityJson { get; set; } = "{}";
}

public sealed class TimelineSceneDropMoveViewModel
{
    public int ProjectID { get; set; }
    public int? BookID { get; set; }
    public int SceneID { get; set; }
    public int AnchorSceneID { get; set; }
    public int? TargetChapterID { get; set; }
    public string Position { get; set; } = "After";
    public int? ReturnSelectedSceneID { get; set; }
}

public sealed class TimelineSceneCharacterDropViewModel
{
    public int SceneID { get; set; }
    public int? CharacterID { get; set; }
    public int? RoleInSceneTypeID { get; set; }
    public int? PresenceTypeID { get; set; }
    public int? LocationID { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class TimelineAssetEventDropViewModel
{
    public int SceneID { get; set; }
    public int? StoryAssetID { get; set; }
    public int AssetEventTypeID { get; set; }
    public int? FromStateID { get; set; }
    public int? ToStateID { get; set; }
    public string EventTitle { get; set; } = string.Empty;
    public string? EventDescription { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public int? LocationID { get; set; }
    public string? CustodyNote { get; set; }
}

public sealed class TimelineAssetDependencyDropViewModel
{
    public int ProjectID { get; set; }
    public int SourceAssetID { get; set; }
    public int TargetAssetID { get; set; }
    public int AssetDependencyTypeID { get; set; }
    public int? SourceRequiredStateID { get; set; }
    public int? TargetBlockedStateID { get; set; }
    public string? Description { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class TimelineLocationRelationshipDropViewModel
{
    public int ProjectID { get; set; }
    public int FromLocationID { get; set; }
    public int ToLocationID { get; set; }
    public int LocationRelationshipTypeID { get; set; }
    public bool IsBidirectional { get; set; }
    public int? Strength { get; set; }
    public bool CanHearNormalSpeech { get; set; }
    public bool CanHearLoudNoise { get; set; }
    public bool CanSeeMovement { get; set; }
    public bool CanSeeClearly { get; set; }
    public bool CanTravelDirectly { get; set; }
    public string? Notes { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class BookTimelineViewModel
{
    public Book Book { get; set; } = new();
    public IReadOnlyList<ChapterTimelineViewModel> Chapters { get; set; } = [];
}

public sealed class ChapterTimelineViewModel
{
    public Chapter Chapter { get; set; } = new();
    public IReadOnlyList<Scene> Scenes { get; set; } = [];
}

public sealed class SceneMetricInputViewModel
{
    public int MetricTypeID { get; set; }
    public string MetricName { get; set; } = string.Empty;
    public string? Description { get; set; }
    public int MinValue { get; set; }
    public int MaxValue { get; set; }
    public int Value { get; set; }
    public string? Notes { get; set; }
}

public sealed class MetricGraphViewModel
{
    public int MetricTypeID { get; set; }
    public string MetricName { get; set; } = string.Empty;
    public int MinValue { get; set; }
    public int MaxValue { get; set; }
    public IReadOnlyList<MetricGraphPointViewModel> Points { get; set; } = [];
}

public sealed class SceneMetricValueUpdateViewModel
{
    public int SceneID { get; set; }
    public int MetricTypeID { get; set; }
    public int MetricID { get; set; }
    public int Value { get; set; }
}

public sealed class MetricGraphPointViewModel
{
    public int SceneID { get; set; }
    public decimal SceneNumber { get; set; }
    public string SceneTitle { get; set; } = string.Empty;
    public int Value { get; set; }
    public int Percent { get; set; }
}

public sealed class PlotLineEditViewModel : IValidatableObject
{
    public int PlotLineID { get; set; }
    public int ProjectID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Plot line name")]
    public string PlotLineName { get; set; } = string.Empty;

    [Display(Name = "Type")]
    public int PlotLineTypeID { get; set; }

    [Range(1, int.MaxValue, ErrorMessage = "Choose a plot importance.")]
    [Display(Name = "Importance")]
    public int PlotImportanceID { get; set; } = 2;

    public int? BookID { get; set; }
    public string? Description { get; set; }

    [Display(Name = "Parent plot line")]
    public int? ParentPlotLineID { get; set; }

    [Display(Name = "Emerges from plot line")]
    public int? EmergesFromPlotLineID { get; set; }

    [Display(Name = "Sort order")]
    public int SortOrder { get; set; }

    [Display(Name = "Colour")]
    public string Colour { get; set; } = "#2f6f63";

    [Display(Name = "Visible on timeline")]
    public bool IsVisibleOnTimeline { get; set; } = true;

    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> PlotImportance { get; set; } = [];
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ParentPlotLineOptions { get; set; } = [];

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (PlotLineID > 0 && EmergesFromPlotLineID == PlotLineID)
        {
            yield return new ValidationResult(
                "A plot line cannot emerge from itself.",
                [nameof(EmergesFromPlotLineID)]);
        }
    }
}

public sealed class PlotLineListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<PlotLineItem> PlotLines { get; set; } = [];
}

public sealed class PlotThreadEditViewModel
{
    public int PlotThreadID { get; set; }
    public int ProjectID { get; set; }

    [Display(Name = "Plot line")]
    public int PlotLineID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Thread title")]
    public string ThreadTitle { get; set; } = string.Empty;

    [Display(Name = "Type")]
    public int ThreadTypeID { get; set; }

    [Display(Name = "Status")]
    public int ThreadStatusID { get; set; }

    [Range(1, 10)]
    public int Importance { get; set; } = 5;

    public string? Summary { get; set; }

    [Display(Name = "Introduced scene")]
    public int? IntroducedSceneID { get; set; }

    [Display(Name = "Planned resolution scene")]
    public int? PlannedResolutionSceneID { get; set; }

    [Display(Name = "Actual resolution scene")]
    public int? ActualResolutionSceneID { get; set; }

    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> PlotLineOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ThreadTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ThreadStatusOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneOptions { get; set; } = [];
}

public sealed class PlotThreadListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<PlotThread> PlotThreads { get; set; } = [];
}

public sealed class ThreadEventCreateViewModel
{
    public int SceneID { get; set; }
    public int? PlotLineID { get; set; }
    public int? PlotThreadID { get; set; }
    public string? NewThreadTitle { get; set; }
    public int? NewThreadTypeID { get; set; }
    public int EventTypeID { get; set; }
    [Range(1, int.MaxValue, ErrorMessage = "Choose an event type.")]
    public int PlotEventTypeID { get; set; } = 2;
    public int? TargetPlotLineID { get; set; }
    public List<int> SelectedTargetPlotLineIDs { get; set; } = [];
    public string EventTitle { get; set; } = string.Empty;
    public string? EventDescription { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class ThreadEventEditViewModel
{
    public int ThreadEventID { get; set; }
    public int SceneID { get; set; }
    public int PlotThreadID { get; set; }
    public int EventTypeID { get; set; }
    [Range(1, int.MaxValue, ErrorMessage = "Choose an event type.")]
    public int PlotEventTypeID { get; set; } = 2;
    public int? TargetPlotLineID { get; set; }
    public List<int> SelectedTargetPlotLineIDs { get; set; } = [];
    public string EventTitle { get; set; } = string.Empty;
    public string? EventDescription { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class PlotLineLaneViewModel
{
    public PlotLineItem PlotLine { get; set; } = new();
    public IReadOnlyList<PlotLaneSceneViewModel> SceneSlots { get; set; } = [];
}

public sealed class PlotLaneSceneViewModel
{
    public Scene Scene { get; set; } = new();
    public IReadOnlyList<ThreadEvent> Events { get; set; } = [];
}

public sealed class StoryAssetListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<StoryAsset> Assets { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetDependencyTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
}

public sealed class StoryAssetEditViewModel
{
    public int StoryAssetID { get; set; }
    public int ProjectID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Asset name")]
    public string AssetName { get; set; } = string.Empty;

    public List<string> Aliases { get; set; } = [];

    [Display(Name = "Kind")]
    public int AssetKindID { get; set; }

    public string? Description { get; set; }

    [Range(1, 10)]
    public int Importance { get; set; } = 5;

    [Display(Name = "Current state")]
    public int? CurrentStateID { get; set; }

    [Display(Name = "Current location")]
    public int? CurrentLocationID { get; set; }

    [Display(Name = "Resolved")]
    public bool IsResolved { get; set; }

    [Display(Name = "Show in Quick Add Bar")]
    public bool ShowInQuickAddBar { get; set; }

    public string? ImagePath { get; set; }
    public string? ThumbnailPath { get; set; }

    [Display(Name = "Upload image")]
    public IFormFile? ImageUpload { get; set; }

    [Display(Name = "Remove image")]
    public bool RemoveImage { get; set; }

    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> AssetKindOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
}

public sealed class StoryAssetDetailViewModel
{
    public Project Project { get; set; } = new();
    public StoryAsset Asset { get; set; } = new();
    public IReadOnlyList<StoryAsset> ProjectAssets { get; set; } = [];
    public IReadOnlyList<AssetEvent> Events { get; set; } = [];
    public IReadOnlyList<AssetDependency> Dependencies { get; set; } = [];
    public IReadOnlyList<AssetCustodyEvent> CustodyEvents { get; set; } = [];
    public AssetDependencyEditViewModel NewDependency { get; set; } = new();
    public IReadOnlyList<SelectListItem> AssetOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetDependencyTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
}

public sealed class AssetEventCreateViewModel
{
    public int SceneID { get; set; }
    public int? StoryAssetID { get; set; }
    public string? NewAssetName { get; set; }
    public int? NewAssetKindID { get; set; }
    public int AssetEventTypeID { get; set; }
    public int? FromStateID { get; set; }
    public int? ToStateID { get; set; }
    public string EventTitle { get; set; } = string.Empty;
    public string? EventDescription { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class AssetCustodyCreateViewModel
{
    public int SceneID { get; set; }
    public int StoryAssetID { get; set; }
    public int AssetCustodyEventTypeID { get; set; }
    public int CustodyRoleID { get; set; }
    public List<int> CustodianCharacterIds { get; set; } = [];
    public string CustodianNames { get; set; } = string.Empty;
    public string? Description { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class SceneAssetLocationCreateViewModel
{
    public int SceneAssetLocationID { get; set; }
    public int SceneID { get; set; }
    public int StoryAssetID { get; set; }
    public int LocationID { get; set; }
    public string? Description { get; set; }
    public bool UpdateCurrentLocation { get; set; } = true;
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class AssetDependencyEditViewModel
{
    public int AssetDependencyID { get; set; }
    public int ProjectID { get; set; }
    public int SourceAssetID { get; set; }
    public int TargetAssetID { get; set; }
    public int AssetDependencyTypeID { get; set; }
    public int? SourceRequiredStateID { get; set; }
    public int? TargetBlockedStateID { get; set; }
    public string? Description { get; set; }
}

public sealed class AssetLaneViewModel
{
    public StoryAsset Asset { get; set; } = new();
    public IReadOnlyList<AssetLaneSceneViewModel> SceneSlots { get; set; } = [];
}

public sealed class AssetLaneSceneViewModel
{
    public Scene Scene { get; set; } = new();
    public IReadOnlyList<AssetEvent> Events { get; set; } = [];
}

public sealed class CharacterListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<Character> Characters { get; set; } = [];
}

public sealed class CharacterEditViewModel
{
    public const int CustomSexValue = -1;

    public int CharacterID { get; set; }
    public int ProjectID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Display name")]
    public string CharacterName { get; set; } = string.Empty;

    public List<string> Aliases { get; set; } = [];

    [Display(Name = "Sex")]
    public int? SexValueID { get; set; }

    public string? Sex { get; set; }

    [Display(Name = "Add custom sex")]
    [StringLength(100)]
    public string? CustomSex { get; set; }

    [Display(Name = "Birth date")]
    public DateTime? BirthDate { get; set; }

    [Display(Name = "Age at series start")]
    public int? AgeAtSeriesStart { get; set; }

    public string? Height { get; set; }

    [Display(Name = "Eye colour")]
    public string? EyeColour { get; set; }

    [Display(Name = "Story importance")]
    public int? CharacterImportance { get; set; }

    [Display(Name = "Show in Quick Add Bar")]
    public bool ShowInQuickAddBar { get; set; }

    [Display(Name = "Default description")]
    public string? DefaultDescription { get; set; }

    public string? ImagePath { get; set; }
    public string? ThumbnailPath { get; set; }
    public int? AvatarSourceCharacterImageID { get; set; }
    public string? AvatarImagePath { get; set; }
    public string? AvatarThumbnailPath { get; set; }

    [Display(Name = "Upload image")]
    public IFormFile? ImageUpload { get; set; }

    [Display(Name = "Remove image")]
    public bool RemoveImage { get; set; }

    public IReadOnlyList<SelectListItem> ImportanceOptions { get; set; } =
    [
        new("Use timeline fallback", string.Empty),
        new("Major", "10"),
        new("Secondary / Supporting", "6"),
        new("Minor", "2")
    ];

    public IReadOnlyList<SelectListItem> SexOptions { get; set; } = [];

    public Project? Project { get; set; }
}

public sealed class CharacterDetailViewModel
{
    public Project Project { get; set; } = new();
    public Character Character { get; set; } = new();
    public IReadOnlyList<CharacterAlias> Aliases { get; set; } = [];
    public IReadOnlyList<Character> ProjectCharacters { get; set; } = [];
    public IReadOnlyList<CharacterImage> Images { get; set; } = [];
    public string DisplayAge { get; set; } = "Unknown";
    public IReadOnlyList<SceneCharacter> Appearances { get; set; } = [];
    public IReadOnlyList<CharacterAttributeEvent> AttributeEvents { get; set; } = [];
    public IReadOnlyList<CharacterKnowledgeItem> KnowledgeItems { get; set; } = [];
    public IReadOnlyList<CharacterRelationship> InitialRelationships { get; set; } = [];
    public IReadOnlyList<CurrentRelationshipViewModel> CurrentRelationships { get; set; } = [];
    public IReadOnlyList<RelationshipEvent> RelationshipEvents { get; set; } = [];
    public InitialRelationshipEditViewModel NewInitialRelationship { get; set; } = new();
    public IReadOnlyList<SelectListItem> CharacterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipStateOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
}

public sealed class CharacterImageUploadViewModel
{
    public int CharacterID { get; set; }

    [Display(Name = "Image")]
    public IFormFile? ImageUpload { get; set; }

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

public sealed class CharacterAvatarCropViewModel
{
    public int CharacterID { get; set; }
    public int CharacterImageID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public string ImagePath { get; set; } = string.Empty;
    public string? Caption { get; set; }
    public int CropX { get; set; }
    public int CropY { get; set; }
    public int CropSize { get; set; }
}

public sealed class CurrentRelationshipViewModel
{
    public CharacterRelationship Relationship { get; set; } = new();
    public RelationshipEvent? LatestEvent { get; set; }
    public IReadOnlyList<RelationshipEvent> Events { get; set; } = [];
    public string CurrentStateName => LatestEvent?.RelationshipStateName
        ?? Relationship.InitialRelationshipStateName
        ?? Relationship.RelationshipTypeName;
    public int? CurrentIntensity => LatestEvent?.Intensity ?? Relationship.InitialIntensity;
    public string? CurrentNotes => LatestEvent?.Description ?? Relationship.Notes;
}

public sealed class RelationshipMapViewModel
{
    public Project Project { get; set; } = new();
    public int? BookID { get; set; }
    public int? ChapterID { get; set; }
    public int? SceneID { get; set; }
    public int? FocusCharacterID { get; set; }
    public bool ShowFullNetwork { get; set; }
    public string StoryPointLabel { get; set; } = "Project Start";
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SceneOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CharacterOptions { get; set; } = [];
    public IReadOnlyList<RelationshipMapCategoryViewModel> Categories { get; set; } = [];
    public IReadOnlyList<RelationshipMapNodeViewModel> Nodes { get; set; } = [];
    public IReadOnlyList<RelationshipMapLinkViewModel> Links { get; set; } = [];
    public IReadOnlyList<RelationshipMapRelationshipViewModel> Relationships { get; set; } = [];
}

public sealed class RelationshipMapCategoryViewModel
{
    public int RelationshipCategoryID { get; set; }
    public string CategoryName { get; set; } = string.Empty;
    public string CssClass { get; set; } = "relationship-category-other";
}

public sealed class RelationshipMapNodeViewModel
{
    public int CharacterID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public int? CharacterImportance { get; set; }
    public string? AvatarImagePath { get; set; }
    public string? AvatarThumbnailPath { get; set; }
    public string? ImagePath { get; set; }
    public string? ThumbnailPath { get; set; }
    public bool IsFocus { get; set; }
}

public sealed class RelationshipMapLinkViewModel
{
    public int CharacterRelationshipID { get; set; }
    public int SourceCharacterID { get; set; }
    public int TargetCharacterID { get; set; }
    public string RelationshipLabel { get; set; } = string.Empty;
    public string CategoryName { get; set; } = "Other";
    public string CssClass { get; set; } = "relationship-category-other";
    public int? Intensity { get; set; }
}

public sealed class RelationshipMapRelationshipViewModel
{
    public int CharacterRelationshipID { get; set; }
    public int CharacterAID { get; set; }
    public string CharacterAName { get; set; } = string.Empty;
    public int CharacterBID { get; set; }
    public string CharacterBName { get; set; } = string.Empty;
    public string RelationshipTypeName { get; set; } = string.Empty;
    public string RelationshipCategoryName { get; set; } = "Other";
    public string CssClass { get; set; } = "relationship-category-other";
    public string InitialStateName { get; set; } = string.Empty;
    public string CurrentStateName { get; set; } = string.Empty;
    public int? InitialIntensity { get; set; }
    public int? CurrentIntensity { get; set; }
    public bool IsReciprocal { get; set; }
    public string? Notes { get; set; }
    public IReadOnlyList<RelationshipMapHistoryItemViewModel> History { get; set; } = [];
}

public sealed class RelationshipMapHistoryItemViewModel
{
    public string Label { get; set; } = string.Empty;
    public string StateName { get; set; } = string.Empty;
    public int? Intensity { get; set; }
    public string? Notes { get; set; }
    public int? SceneID { get; set; }
}

public sealed class InitialRelationshipEditViewModel
{
    public int CharacterRelationshipID { get; set; }
    public int ProjectID { get; set; }
    public int CharacterID { get; set; }
    public int? ReturnCharacterID { get; set; }
    public int RelatedCharacterID { get; set; }
    public int RelationshipTypeID { get; set; }
    public int? InitialBookID { get; set; }
    public int? InitialRelationshipStateID { get; set; }
    public int? InitialIntensity { get; set; }
    public bool IsReciprocal { get; set; }
    public bool ReaderInitiallyKnows { get; set; }
    public string? Notes { get; set; }
}

public sealed class SceneCharacterCreateViewModel
{
    public int SceneID { get; set; }
    public int? CharacterID { get; set; }
    public string? NewCharacterName { get; set; }
    public int? RoleInSceneTypeID { get; set; }
    public int? PresenceTypeID { get; set; }
    public int? LocationID { get; set; }
    public int? EntryLocationID { get; set; }
    public int? ExitLocationID { get; set; }
    public string? AppearanceNotes { get; set; }
    public string? OutfitDescription { get; set; }
    public string? PhysicalCondition { get; set; }
    public string? EmotionalState { get; set; }
    public string? KnowledgeNotes { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class SceneCharacterEditViewModel
{
    public int SceneCharacterID { get; set; }
    public int SceneID { get; set; }
    public int CharacterID { get; set; }
    public int? RoleInSceneTypeID { get; set; }
    public int? PresenceTypeID { get; set; }
    public int? LocationID { get; set; }
    public int? EntryLocationID { get; set; }
    public int? ExitLocationID { get; set; }
    public string? AppearanceNotes { get; set; }
    public string? OutfitDescription { get; set; }
    public string? PhysicalCondition { get; set; }
    public string? EmotionalState { get; set; }
    public string? KnowledgeNotes { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class CharacterAttributeEventCreateViewModel
{
    public int CharacterAttributeEventID { get; set; }
    public int SceneID { get; set; }
    public int CharacterID { get; set; }
    public int CharacterAttributeTypeID { get; set; }
    public string AttributeValue { get; set; } = string.Empty;
    public string? Description { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class CharacterKnowledgeCreateViewModel
{
    public int CharacterKnowledgeID { get; set; }
    public int SceneID { get; set; }
    public int CharacterID { get; set; }
    public int? StoryAssetID { get; set; }
    public int? PlotThreadID { get; set; }
    public int KnowledgeStateID { get; set; }
    public int? SourceEventID { get; set; }
    public string? Description { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class RelationshipEventCreateViewModel
{
    public int RelationshipEventID { get; set; }
    public int SceneID { get; set; }
    public int? CharacterRelationshipID { get; set; }
    public int? CharacterAID { get; set; }
    public int? CharacterBID { get; set; }
    public int? RelationshipTypeID { get; set; }
    public int RelationshipStateID { get; set; }
    public int? Intensity { get; set; }
    public bool IsKnownToOtherCharacter { get; set; }
    public string? Description { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class CharacterLaneViewModel
{
    public Character Character { get; set; } = new();
    public IReadOnlyList<CharacterLaneSceneViewModel> SceneSlots { get; set; } = [];
}

public sealed class CharacterLaneSceneViewModel
{
    public Scene Scene { get; set; } = new();
    public IReadOnlyList<SceneCharacter> Appearances { get; set; } = [];
}

public sealed class LocationListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<LocationItem> Locations { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipTypeOptions { get; set; } = [];
}

public sealed class LocationEditViewModel
{
    public int LocationID { get; set; }
    public int ProjectID { get; set; }
    public int? ParentLocationID { get; set; }

    [Required, StringLength(200)]
    [Display(Name = "Location name")]
    public string LocationName { get; set; } = string.Empty;

    public List<string> Aliases { get; set; } = [];

    [Display(Name = "Location type")]
    public int? LocationTypeID { get; set; }

    public string? Description { get; set; }
    [Display(Name = "Show in Quick Add Bar")]
    public bool ShowInQuickAddBar { get; set; }
    [Display(Name = "Exclude from Word Companion detection")]
    public bool ExcludeFromCompanionDetection { get; set; }
    [Display(Name = "Detection priority")]
    [Range(0, 1000, ErrorMessage = "Detection priority must be between 0 and 1000.")]
    public int DetectionPriority { get; set; } = 50;
    public Project? Project { get; set; }
    public IReadOnlyList<SelectListItem> ParentLocationOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> LocationTypeOptions { get; set; } = [];
}

public sealed class LocationDetailViewModel
{
    public Project Project { get; set; } = new();
    public LocationItem Location { get; set; } = new();
    public IReadOnlyList<LocationItem> ChildLocations { get; set; } = [];
    public IReadOnlyList<LocationRelationship> Relationships { get; set; } = [];
    public IReadOnlyList<Scene> Scenes { get; set; } = [];
    public IReadOnlyList<SceneCharacter> Characters { get; set; } = [];
    public IReadOnlyList<SceneAssetLocation> Assets { get; set; } = [];
    public LocationRelationshipEditViewModel NewRelationship { get; set; } = new();
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> RelationshipTypeOptions { get; set; } = [];
}

public sealed class LocationRelationshipEditViewModel
{
    public int LocationRelationshipID { get; set; }
    public int ProjectID { get; set; }
    public int FromLocationID { get; set; }
    public int ToLocationID { get; set; }
    public int LocationRelationshipTypeID { get; set; }
    public bool IsBidirectional { get; set; }
    public int? Strength { get; set; }
    public bool CanHearNormalSpeech { get; set; }
    public bool CanHearLoudNoise { get; set; }
    public bool CanSeeMovement { get; set; }
    public bool CanSeeClearly { get; set; }
    public bool CanTravelDirectly { get; set; }
    public string? Notes { get; set; }
}

public sealed class FloorPlanListViewModel
{
    public Project Project { get; set; } = new();
    public IReadOnlyList<FloorPlan> FloorPlans { get; set; } = [];
    public FloorPlanEditViewModel NewFloorPlan { get; set; } = new();
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
}

public sealed class FloorPlanEditViewModel
{
    public int FloorPlanID { get; set; }
    public int ProjectID { get; set; }
    public int? LocationID { get; set; }
    public string? LocationName { get; set; }
    public string? LocationPath { get; set; }

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

    public string? Description { get; set; }
}

public sealed class FloorPlanEditorViewModel
{
    public Project Project { get; set; } = new();
    public FloorPlanEditViewModel FloorPlan { get; set; } = new();
    public List<FloorPlanFloorEditViewModel> Floors { get; set; } = [];
    public List<FloorPlanBlockEditViewModel> Blocks { get; set; } = [];
    public List<FloorPlanTransitionEditViewModel> Transitions { get; set; } = [];
    public FloorPlanFloorEditViewModel NewFloor { get; set; } = new();
    public FloorPlanBlockEditViewModel NewBlock { get; set; } = new();
    public FloorPlanTransitionEditViewModel NewTransition { get; set; } = new();
    public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
    public IReadOnlyList<LocationItem> Locations { get; set; } = [];
    public IReadOnlyList<SelectListItem> BlockTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> TransitionTypeOptions { get; set; } = [];
    public IReadOnlyList<FloorPlanOccupancySceneViewModel> OccupancyScenes { get; set; } = [];
    public IReadOnlyList<FloorPlanOccupancyTokenViewModel> OccupancyTokens { get; set; } = [];
    public IReadOnlyList<FloorPlanOccupancyWarningViewModel> OccupancyWarnings { get; set; } = [];
}

public sealed class FloorPlanOccupancySceneViewModel
{
    public int SceneID { get; set; }
    public int? InitialFloorPlanFloorID { get; set; }
    public string Label { get; set; } = string.Empty;
}

public sealed class FloorPlanOccupancyTokenViewModel
{
    public int SceneID { get; set; }
    public int FloorPlanFloorID { get; set; }
    public int LocationID { get; set; }
    public string LocationName { get; set; } = string.Empty;
    public string EntityType { get; set; } = string.Empty;
    public int EntityID { get; set; }
    public string DisplayName { get; set; } = string.Empty;
    public string? ImagePath { get; set; }
    public string? ThumbnailPath { get; set; }
}

public sealed class FloorPlanOccupancyWarningViewModel
{
    public int SceneID { get; set; }
    public int? FloorPlanFloorID { get; set; }
    public string Severity { get; set; } = "warning";
    public string Message { get; set; } = string.Empty;
}

public sealed class FloorPlanFloorEditViewModel
{
    public int FloorPlanFloorID { get; set; }
    public int FloorPlanID { get; set; }
    public int? LocationID { get; set; }
    public string? LocationName { get; set; }
    public string? LocationPath { get; set; }

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

    public string? Description { get; set; }

    public int SortOrder { get; set; }

    [Range(8, 80)]
    public int GridWidth { get; set; } = 24;

    [Range(8, 80)]
    public int GridHeight { get; set; } = 16;

    public string? BackgroundImagePath { get; set; }
    public string? BackgroundImageOriginalFileName { get; set; }

    [Range(0.05, 1.0)]
    public decimal BackgroundOpacity { get; set; } = 0.35m;

    [Range(0.1, 5.0)]
    public decimal BackgroundScale { get; set; } = 1m;

    [Range(-10000, 10000)]
    public decimal BackgroundOffsetX { get; set; }

    [Range(-10000, 10000)]
    public decimal BackgroundOffsetY { get; set; }

    public bool BackgroundLocked { get; set; } = true;
}

public sealed class FloorPlanFloorBackgroundSettingsViewModel
{
    public int FloorPlanID { get; set; }
    public int FloorPlanFloorID { get; set; }

    [Range(0.05, 1.0)]
    public decimal BackgroundOpacity { get; set; } = 0.35m;

    [Range(0.1, 5.0)]
    public decimal BackgroundScale { get; set; } = 1m;

    [Range(-10000, 10000)]
    public decimal BackgroundOffsetX { get; set; }

    [Range(-10000, 10000)]
    public decimal BackgroundOffsetY { get; set; }

    public bool BackgroundLocked { get; set; } = true;
}

public sealed class FloorPlanBlockEditViewModel
{
    public int FloorPlanBlockID { get; set; }
    public int FloorPlanFloorID { get; set; }

    public int? LocationID { get; set; }
    public string AddBlockLocationMode { get; set; } = "Existing";
    public string? NewLocationName { get; set; }

    public string LocationName { get; set; } = string.Empty;
    public string LocationPath { get; set; } = string.Empty;

    [Range(0, 79)]
    public int X { get; set; }

    [Range(0, 79)]
    public int Y { get; set; }

    [Range(1, 80)]
    public int WidthCells { get; set; } = 2;

    [Range(1, 80)]
    public int HeightCells { get; set; } = 1;

    [Required, StringLength(40)]
    public string BlockType { get; set; } = FloorPlanBlockTypes.Room;

    // Future L-shape support should add ShapeType, FootWidthCells, FootHeightCells, and CornerPosition
    // only when the database, save/load flow, rendering, and collision checks can move together.
    [StringLength(200)]
    public string? LabelOverride { get; set; }

    public string? Notes { get; set; }
    public string DisplayLabel => string.IsNullOrWhiteSpace(LabelOverride) ? LocationName : LabelOverride;
}

public sealed class FloorPlanTransitionEditViewModel
{
    public int FloorPlanTransitionID { get; set; }
    public int FloorPlanFloorID { get; set; }

    [Required]
    public int? FromLocationID { get; set; }

    public string FromLocationName { get; set; } = string.Empty;
    public string FromLocationPath { get; set; } = string.Empty;

    [Required]
    public int? ToLocationID { get; set; }

    public string ToLocationName { get; set; } = string.Empty;
    public string ToLocationPath { get; set; } = string.Empty;

    [Required, StringLength(50)]
    public string TransitionType { get; set; } = FloorPlanTransitionTypes.Door;

    public string? Notes { get; set; }
    public bool IsLocked { get; set; }

    [StringLength(255)]
    public string? DisplayLabel { get; set; }

    [StringLength(20)]
    public string PositionWithinLocation { get; set; } = FloorPlanTransitionPositions.Centre;

    public string Label => string.IsNullOrWhiteSpace(DisplayLabel) ? TransitionType : DisplayLabel;
}

public sealed class AvatarViewModel
{
    public string DisplayName { get; set; } = string.Empty;
    public string? ImagePath { get; set; }
    public string? ThumbnailPath { get; set; }
    public string Size { get; set; } = "md";
    public string? CssClass { get; set; }
    public bool Lazy { get; set; } = true;
}

public sealed class WarningDashboardViewModel
{
    public Project Project { get; set; } = new();
    public int? BookID { get; set; }
    public int? ChapterID { get; set; }
    public int? SceneID { get; set; }
    public int? WarningTypeID { get; set; }
    public int? WarningSeverityID { get; set; }
    public string? EntityType { get; set; }
    public string? Category { get; set; }
    public bool IncludeDismissed { get; set; }
    public bool IncludeIntentional { get; set; }
    public bool ShowAcknowledgedWarnings { get; set; }
    public IReadOnlyList<ContinuityWarning> Warnings { get; set; } = [];
    public IReadOnlyList<ProjectWarningViewModel> ProjectWarnings { get; set; } = [];
    public IReadOnlyList<WarningSeverityGroupViewModel> WarningGroups { get; set; } = [];
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> WarningTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> SeverityOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> EntityTypeOptions { get; set; } = [];
    public IReadOnlyList<SelectListItem> CategoryOptions { get; set; } = [];
    public IReadOnlyDictionary<string, int> CountsBySeverity { get; set; } = new Dictionary<string, int>();
    public IReadOnlyDictionary<string, int> CountsByType { get; set; } = new Dictionary<string, int>();
    public ContinuityValidationSummary? LastValidationSummary { get; set; }
    public bool HasAnyWarnings { get; set; }
}

public sealed class SceneContinuityEditorViewModel
{
    public SceneEditViewModel Scene { get; set; } = new();
    public WarningDashboardViewModel Dashboard { get; set; } = new();
}

public sealed class WarningSeverityGroupViewModel
{
    public string Severity { get; set; } = string.Empty;
    public bool IsExpanded { get; set; }
    public IReadOnlyList<ProjectWarningViewModel> Warnings { get; set; } = [];
}

public sealed class ProjectWarningViewModel
{
    public string Source { get; set; } = "Persisted";
    public int? ContinuityWarningID { get; set; }
    public string WarningType { get; set; } = string.Empty;
    public string WarningTypeDisplayName { get; set; } = string.Empty;
    public string Category { get; set; } = "General";
    public string Severity { get; set; } = "Information";
    public string Message { get; set; } = string.Empty;
    public string? Details { get; set; }
    public int? BookID { get; set; }
    public string BookDisplayName { get; set; } = string.Empty;
    public int? ChapterID { get; set; }
    public string ChapterDisplayName { get; set; } = string.Empty;
    public int? SceneID { get; set; }
    public string SceneDisplayName { get; set; } = string.Empty;
    public string ContextSceneDisplayName { get; set; } = string.Empty;
    public int? CharacterID { get; set; }
    public string CharacterName { get; set; } = string.Empty;
    public int? AssetID { get; set; }
    public string EntityType { get; set; } = string.Empty;
    public bool IsDismissed { get; set; }
    public bool IsIntentional { get; set; }
    public bool IsAcknowledged { get; set; }
    public string? AcknowledgementNotes { get; set; }
    public string WarningKey { get; set; } = string.Empty;
    public int? KnowledgeId { get; set; }
    public string KnowledgeTitle { get; set; } = string.Empty;
    public string KnowledgeDescription { get; set; } = string.Empty;
    public string PreviousKnowledgeState { get; set; } = string.Empty;
    public string CurrentKnowledgeState { get; set; } = string.Empty;
    public int? PreviousBookId { get; set; }
    public int? PreviousChapterId { get; set; }
    public int? PreviousSceneId { get; set; }
    public string PreviousSceneDisplayName { get; set; } = string.Empty;
    public string PreviousContextSceneDisplayName { get; set; } = string.Empty;
    public int? CurrentBookId { get; set; }
    public int? CurrentChapterId { get; set; }
    public int? CurrentSceneId { get; set; }
    public string CurrentSceneDisplayName { get; set; } = string.Empty;
    public string CurrentContextSceneDisplayName { get; set; } = string.Empty;
    public int? EstablishedSceneId { get; set; }
    public string EstablishedSceneDisplayName { get; set; } = string.Empty;
    public string EstablishedContextSceneDisplayName { get; set; } = string.Empty;
    public int? ReferencedSceneId { get; set; }
    public string ReferencedSceneDisplayName { get; set; } = string.Empty;
    public string ReferencedContextSceneDisplayName { get; set; } = string.Empty;
    public string InvestigationDisplayMode { get; set; } = string.Empty;
    public int? InvestigationSceneId { get; set; }
    public bool CanInvestigate { get; set; }
    public DateTime LastDetectedDate { get; set; }
}

public sealed class WarningDashboardReturnFilter
{
    public int ProjectID { get; set; }
    public int? BookID { get; set; }
    public int? ChapterID { get; set; }
    public int? SceneID { get; set; }
    public int? WarningTypeID { get; set; }
    public int? WarningSeverityID { get; set; }
    public string? EntityType { get; set; }
    public string? Category { get; set; }
    public bool IncludeDismissed { get; set; }
    public bool IncludeIntentional { get; set; }
    public bool ShowAcknowledgedWarnings { get; set; }
}

public sealed class SceneDependencyCreateViewModel
{
    public int SceneDependencyID { get; set; }
    public int ProjectID { get; set; }
    public int CurrentSceneID { get; set; }
    public int RelatedSceneID { get; set; }
    public string Direction { get; set; } = "CurrentDependsOnRelated";
    public int SceneDependencyTypeID { get; set; }
    public bool IsHardDependency { get; set; }
    public string? Description { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
}

public sealed class SceneMoveRequestViewModel
{
    public int SceneID { get; set; }
    public int TargetChapterID { get; set; }
    public string Position { get; set; } = "End";
    public bool RenumberScenes { get; set; }
    public int? ReturnProjectID { get; set; }
    public int? ReturnBookID { get; set; }
    public string? ReturnUrl { get; set; }
}

public sealed class SceneMovePreviewViewModel
{
    public SceneMovePreview Preview { get; set; } = new();
    public SceneMoveRequestViewModel Request { get; set; } = new();
    public IReadOnlyList<ContinuityWarning> CurrentWarnings { get; set; } = [];
}

public sealed class AnalyticsViewModel
{
    public Project Project { get; set; } = new();
    public int? BookID { get; set; }
    public decimal? StartChapterNumber { get; set; }
    public decimal? EndChapterNumber { get; set; }
    public decimal? StartSceneNumber { get; set; }
    public decimal? EndSceneNumber { get; set; }
    public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
    public IReadOnlyList<AnalyticsMetricGraphViewModel> MetricGraphs { get; set; } = [];
    public IReadOnlyList<AnalyticsRevisionSummary> RevisionSummary { get; set; } = [];
    public IReadOnlyList<AnalyticsRevisionScene> RevisionAttentionScenes { get; set; } = [];
    public IReadOnlyList<AnalyticsPurposeSummary> PurposeSummary { get; set; } = [];
    public IReadOnlyList<AnalyticsSceneGap> SceneGaps { get; set; } = [];
    public IReadOnlyList<AnalyticsThreadHealth> ThreadHealth { get; set; } = [];
    public IReadOnlyList<AnalyticsAssetHealth> AssetHealth { get; set; } = [];
    public IReadOnlyList<AnalyticsWarningSeverityCount> WarningSeverityCounts { get; set; } = [];
    public IReadOnlyList<AnalyticsWarningTypeCount> WarningTypeCounts { get; set; } = [];
    public IReadOnlyList<AnalyticsWarningHotspot> WarningHotspots { get; set; } = [];
    public IReadOnlyList<AnalyticsPovSummary> PovSummary { get; set; } = [];
    public IReadOnlyList<AnalyticsMultiPovChapter> MultiPovChapters { get; set; } = [];
    public IReadOnlyList<AnalyticsCharacterAppearance> CharacterAppearances { get; set; } = [];
    public IReadOnlyList<AnalyticsBusyScene> BusyScenes { get; set; } = [];
    public IReadOnlyList<AnalyticsLocationUsage> LocationUsage { get; set; } = [];
    public IReadOnlyList<AnalyticsReviewNote> PacingReviewNotes { get; set; } = [];
}

public sealed class AnalyticsMetricGraphViewModel
{
    public string MetricName { get; set; } = string.Empty;
    public IReadOnlyList<AnalyticsMetricPoint> Points { get; set; } = [];
}

public sealed class AnalyticsReviewNote
{
    public string MetricName { get; set; } = string.Empty;
    public string SceneRange { get; set; } = string.Empty;
    public string ValuePattern { get; set; } = string.Empty;
    public string ReviewNote { get; set; } = string.Empty;
    public int? SceneID { get; set; }
}
