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

namespace PlotLine.ViewModels;

public sealed class OnboardingWizardViewModel
{
    public string CurrentStep { get; set; } = OnboardingSteps.Welcome;
    public int StepNumber { get; set; } = 1;
    public int TotalSteps { get; set; } = 6;
    public string? WritingJourney { get; set; }
    public string? WritingSoftware { get; set; }
    public int? ProjectID { get; set; }
    public int? BookID { get; set; }
    public string? SelectedProjectName { get; set; }
    public string? SelectedBookTitle { get; set; }
    public bool IsMicrosoftWordPath => string.Equals(WritingSoftware, WritingSoftwareValues.MicrosoftWord, StringComparison.Ordinal);
    public CompanionPresenceViewModel CompanionPresence { get; set; } = new();
    public ManuscriptScanStateViewModel ScanState { get; set; } = new();
    public IReadOnlyList<OnboardingOptionViewModel> Options { get; set; } = [];
    public IReadOnlyList<OnboardingProjectOptionViewModel> ProjectOptions { get; set; } = [];
    public IReadOnlyList<OnboardingBookOptionViewModel> BookOptions { get; set; } = [];
    public ProjectOnboardingForm ProjectForm { get; set; } = new();
    public BookOnboardingForm BookForm { get; set; } = new();
}

public sealed class OnboardingOptionViewModel
{
    public string Value { get; init; } = string.Empty;
    public string Title { get; init; } = string.Empty;
    public string Description { get; init; } = string.Empty;
    public string? Badge { get; init; }
}

public sealed class OnboardingJourneyHeaderViewModel
{
    public int CurrentStep { get; init; }
    public int TotalSteps { get; init; } = 9;
    public string AriaLabel { get; init; } = "Manuscript import progress";
}

public sealed class StoryIntelligencePipelineHeaderViewModel
{
    public string CurrentStage { get; init; } = "Analyse Manuscript";
}

public sealed class WritingJourneyOnboardingForm
{
    [Required(ErrorMessage = "Choose the closest writing stage before continuing.")]
    public string? WritingJourney { get; set; }
}

public sealed class WritingSoftwareOnboardingForm
{
    [Required(ErrorMessage = "Choose the software you write in before continuing.")]
    public string? WritingSoftware { get; set; }
}

public static class OnboardingSelectionModes
{
    public const string Create = "Create";
    public const string Select = "Select";
}

public sealed class ProjectOnboardingForm
{
    [Required]
    public string Mode { get; set; } = OnboardingSelectionModes.Create;

    public int? SelectedProjectID { get; set; }

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

    public string? Description { get; set; }
}

public sealed class BookOnboardingForm
{
    [Required]
    public string Mode { get; set; } = OnboardingSelectionModes.Create;

    public int? SelectedBookID { get; set; }

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

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

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

public sealed class OnboardingProjectOptionViewModel
{
    public int ProjectID { get; init; }
    public string ProjectName { get; init; } = string.Empty;
    public string Description { get; init; } = string.Empty;
}

public sealed class OnboardingBookOptionViewModel
{
    public int BookID { get; init; }
    public string DisplayTitle { get; init; } = string.Empty;
    public string Detail { get; init; } = string.Empty;
}

public sealed class OnboardingNudgeViewModel
{
    public bool ShouldShow { get; init; }
    public string ButtonText { get; init; } = "Start setup";
    public string Title { get; init; } = "Set up PlotDirector around the way you write";
    public string Description { get; init; } = "Answer a few quick questions so PlotDirector can guide your first project.";
    public bool IsCompanionPresence { get; init; }
    public bool CompanionConnected { get; init; }
}

public sealed class StoryIntelligenceOverviewViewModel
{
    public Guid? PreviewID { get; init; }
    public int? ProjectID { get; init; }
    public int? BookID { get; init; }
    public string? SelectedProjectName { get; init; }
    public string? SelectedBookTitle { get; init; }
    public int ApprovedChapterCount { get; init; }
    public int ApprovedWordCount { get; init; }
    public int MissingChapterTextCount { get; init; }
    public string? Message { get; init; }
    public Guid? ExistingBatchID { get; init; }
    public StoryIntelligenceJobProgress? ExistingJob { get; init; }
    public bool CanStart { get; init; }
    public string AppearancePreference { get; init; } = StoryMemoryAppearancePreferences.Balanced;
}

public sealed class StoryIntelligenceProgressViewModel
{
    public StoryIntelligenceJobProgress Job { get; init; } = new();
    public Guid? BatchID { get; init; }
    public Guid? PreviewID { get; init; }
    public int? ImportSessionID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public string? ProjectName { get; init; }
    public string? BookTitle { get; init; }
    public IReadOnlyList<StoryIntelligenceOnboardingChapterViewModel> Chapters { get; init; } = [];
    public StoryIntelligenceCharacterReviewViewModel CharacterReview { get; init; } = new();
    public StoryIntelligenceLocationReviewViewModel LocationReview { get; init; } = new();
    public StoryIntelligenceAssetReviewViewModel AssetReview { get; init; } = new();
    public StoryIntelligenceRelationshipReviewViewModel RelationshipReview { get; init; } = new();
    public StoryIntelligenceKnowledgeReviewViewModel KnowledgeReview { get; init; } = new();
    public StoryIntelligencePipelineDashboardViewModel PipelineDashboard { get; init; } = new();
    public int ChapterCount => Chapters.Count;
    public int CompletedChapterCount => Chapters.Count(chapter => chapter.IsRunComplete);
    public int CommittedChapterCount => Chapters.Count(chapter => chapter.HasCompletedCommit);
    public int FailedChapterCount => Chapters.Count(chapter => chapter.IsFailed);
    public int TotalDetectedScenes => Chapters.Sum(chapter => chapter.TotalDetectedScenes ?? 0);
    public int TotalCompletedScenes => Chapters.Sum(chapter => chapter.CompletedScenes ?? 0);
    public int TotalFailedScenes => Chapters.Sum(chapter => chapter.FailedScenes ?? 0);
    public int TotalAnalysedWords => Chapters.Sum(chapter => chapter.SourceWordCount ?? 0);
    public decimal? TotalEstimatedCostUSD => Chapters.Any(chapter => chapter.EstimatedCostUSD.HasValue) ? Chapters.Sum(chapter => chapter.EstimatedCostUSD ?? 0m) : null;
    public long TotalDurationMs => Chapters.Sum(chapter => chapter.TotalDurationMs ?? 0);
    public bool HasActiveRuns => Chapters.Any(chapter => chapter.IsActive);
    public bool AllCommitted => Chapters.Count > 0 && Chapters.All(chapter => chapter.HasCompletedCommit);
    public bool HasReadyScenesToCreate => Chapters.Any(chapter => chapter.CanCommit);
    public bool HasCharactersToReview => CharacterReview.Candidates.Count > 0;
    public bool HasLocationsToReview => LocationReview.Candidates.Count > 0;
    public bool HasAssetsToReview => AssetReview.Candidates.Count > 0;
    public bool HasRelationshipsToReview => RelationshipReview.Candidates.Count > 0;
    public bool HasKnowledgeToReview => KnowledgeReview.Candidates.Count > 0;
}

public sealed class StoryIntelligencePipelineDashboardViewModel
{
    public int ChaptersAnalysed { get; init; }
    public int ScenesImported { get; init; }
    public int CharactersIdentified { get; init; }
    public int CharactersCreatedOrLinked { get; init; }
    public bool CharacterStageComplete { get; init; }
    public int LocationsIdentified { get; init; }
    public int LocationsCreatedOrLinked { get; init; }
    public bool LocationStageComplete { get; init; }
    public int AssetsIdentified { get; init; }
    public int AssetsCreatedOrLinked { get; init; }
    public bool AssetStageComplete { get; init; }
    public int RelationshipsIdentified { get; init; }
    public int RelationshipsCreatedOrLinked { get; init; }
    public bool RelationshipStageComplete { get; init; }
    public int KnowledgeIdentified { get; init; }
    public int KnowledgeCreatedOrLinked { get; init; }
    public bool KnowledgeStageComplete { get; init; }
}

public sealed class StoryIntelligenceCharacterReviewViewModel
{
    public bool CanImport { get; init; }
    public bool HasCommittedScenes { get; init; }
    public int AlreadyLinkedCount { get; init; }
    public bool IsComplete { get; init; }
    public IReadOnlyList<StoryIntelligenceCharacterReviewCandidateViewModel> Candidates { get; init; } = [];
}

public sealed class StoryIntelligenceCharacterReviewCandidateViewModel
{
    public string Key { get; init; } = string.Empty;
    public string CharacterName { get; init; } = string.Empty;
    public string ImportName { get; init; } = string.Empty;
    public int AppearsInScenes { get; init; }
    public string Confidence { get; init; } = "Unknown";
    public IReadOnlyList<string> PossibleAliases { get; init; } = [];
    public string? ExampleFirstAppearance { get; init; }
    public int? ExistingCharacterID { get; init; }
    public string? ExistingCharacterName { get; init; }
    public bool IsExistingMatch => ExistingCharacterID.HasValue;
    public string ActionLabel => IsExistingMatch ? "Link existing" : "Create";
}

public sealed class StoryIntelligenceCharacterImportForm
{
    public Guid BatchID { get; set; }
    public List<StoryIntelligenceCharacterImportChoiceForm> Characters { get; set; } = [];
}

public sealed class StoryIntelligenceCharacterImportChoiceForm
{
    public string Key { get; set; } = string.Empty;
    public string Action { get; set; } = StoryIntelligenceCharacterImportActions.CreateNew;
    public string? ImportName { get; set; }
    public string? AliasTargetKey { get; set; }
}

public static class StoryIntelligenceCharacterImportActions
{
    public const string CreateNew = "CreateNew";
    public const string LinkExisting = "LinkExisting";
    public const string Ignore = "Ignore";
    public const string Alias = "Alias";
}

public sealed class StoryIntelligenceLocationReviewViewModel
{
    public bool CanImport { get; init; }
    public bool HasCommittedScenes { get; init; }
    public int AlreadyLinkedCount { get; init; }
    public bool IsComplete { get; init; }
    public IReadOnlyList<StoryIntelligenceLocationReviewCandidateViewModel> Candidates { get; init; } = [];
}

public sealed class StoryIntelligenceLocationReviewCandidateViewModel
{
    public string Key { get; init; } = string.Empty;
    public string LocationName { get; init; } = string.Empty;
    public string ImportName { get; init; } = string.Empty;
    public string Category { get; init; } = "Ambiguous location";
    public int AppearsInScenes { get; init; }
    public string Confidence { get; init; } = "Unknown";
    public string? ParentLocationHint { get; init; }
    public string? ExampleFirstAppearance { get; init; }
    public string? ExampleContext { get; init; }
    public int? ExistingLocationID { get; init; }
    public string? ExistingLocationName { get; init; }
    public bool IsExistingMatch => ExistingLocationID.HasValue;
    public string ActionLabel => IsExistingMatch ? "Link existing" : "Create";
}

public sealed class StoryIntelligenceLocationImportForm
{
    public Guid BatchID { get; set; }
    public List<StoryIntelligenceLocationImportChoiceForm> Locations { get; set; } = [];
}

public sealed class StoryIntelligenceLocationImportChoiceForm
{
    public string Key { get; set; } = string.Empty;
    public string Action { get; set; } = StoryIntelligenceLocationImportActions.CreateNew;
    public string? ImportName { get; set; }
    public string? AliasTargetKey { get; set; }
}

public static class StoryIntelligenceLocationImportActions
{
    public const string CreateNew = "CreateNew";
    public const string LinkExisting = "LinkExisting";
    public const string Ignore = "Ignore";
    public const string Alias = "Alias";
}

public sealed class StoryIntelligenceAssetReviewViewModel
{
    public bool CanImport { get; init; }
    public bool HasCommittedScenes { get; init; }
    public int AlreadyLinkedCount { get; init; }
    public bool IsComplete { get; init; }
    public IReadOnlyList<StoryIntelligenceAssetReviewCandidateViewModel> Candidates { get; init; } = [];
}

public sealed class StoryIntelligenceAssetReviewCandidateViewModel
{
    public string Key { get; init; } = string.Empty;
    public string AssetName { get; init; } = string.Empty;
    public string ImportName { get; init; } = string.Empty;
    public string Category { get; init; } = "Story asset";
    public int AppearsInScenes { get; init; }
    public string Confidence { get; init; } = "Unknown";
    public string? PossibleOwner { get; init; }
    public string? ExampleFirstAppearance { get; init; }
    public string? ExampleContext { get; init; }
    public IReadOnlyList<string> PossibleAliases { get; init; } = [];
    public int? ExistingAssetID { get; init; }
    public string? ExistingAssetName { get; init; }
    public bool IsExistingMatch => ExistingAssetID.HasValue;
    public string ActionLabel => IsExistingMatch ? "Link existing" : "Create";
}

public sealed class StoryIntelligenceAssetImportForm
{
    public Guid BatchID { get; set; }
    public List<StoryIntelligenceAssetImportChoiceForm> Assets { get; set; } = [];
}

public sealed class StoryIntelligenceAssetImportChoiceForm
{
    public string Key { get; set; } = string.Empty;
    public string Action { get; set; } = StoryIntelligenceAssetImportActions.CreateNew;
    public string? ImportName { get; set; }
    public string? AliasTargetKey { get; set; }
}

public static class StoryIntelligenceAssetImportActions
{
    public const string CreateNew = "CreateNew";
    public const string LinkExisting = "LinkExisting";
    public const string Ignore = "Ignore";
    public const string Alias = "Alias";
}

public sealed class StoryIntelligenceRelationshipReviewViewModel
{
    public bool CanImport { get; init; }
    public bool HasCommittedScenes { get; init; }
    public int AlreadyLinkedCount { get; init; }
    public bool IsComplete { get; init; }
    public IReadOnlyList<SelectListItem> RelationshipTypeOptions { get; init; } = [];
    public IReadOnlyList<StoryIntelligenceRelationshipReviewCandidateViewModel> Candidates { get; init; } = [];
}

public sealed class StoryIntelligenceRelationshipReviewCandidateViewModel
{
    public string Key { get; init; } = string.Empty;
    public string CharacterAName { get; init; } = string.Empty;
    public string CharacterBName { get; init; } = string.Empty;
    public string RelationshipType { get; init; } = "Unknown";
    public int RelationshipTypeID { get; init; }
    public int AppearsInScenes { get; init; }
    public string Confidence { get; init; } = "Unknown";
    public string? FirstAppearance { get; init; }
    public string? LastAppearance { get; init; }
    public string? ExampleScene { get; init; }
    public string? ExampleContext { get; init; }
    public int? ExistingRelationshipID { get; init; }
    public string? ExistingRelationshipLabel { get; init; }
    public bool IsExistingMatch => ExistingRelationshipID.HasValue;
    public string ActionLabel => IsExistingMatch ? "Link existing" : "Create";
}

public sealed class StoryIntelligenceRelationshipImportForm
{
    public Guid BatchID { get; set; }
    public List<StoryIntelligenceRelationshipImportChoiceForm> Relationships { get; set; } = [];
}

public sealed class StoryIntelligenceRelationshipImportChoiceForm
{
    public string Key { get; set; } = string.Empty;
    public string Action { get; set; } = StoryIntelligenceRelationshipImportActions.CreateNew;
    public int? RelationshipTypeID { get; set; }
    public string? AliasTargetKey { get; set; }
}

public static class StoryIntelligenceRelationshipImportActions
{
    public const string CreateNew = "CreateNew";
    public const string LinkExisting = "LinkExisting";
    public const string Ignore = "Ignore";
    public const string Alias = "Alias";
}

public sealed class StoryIntelligenceKnowledgeReviewViewModel
{
    public bool CanImport { get; init; }
    public bool HasCommittedScenes { get; init; }
    public int AlreadyLinkedCount { get; init; }
    public bool IsComplete { get; init; }
    public IReadOnlyList<SelectListItem> KnowledgeStateOptions { get; init; } = [];
    public IReadOnlyList<StoryIntelligenceKnowledgeReviewCandidateViewModel> Candidates { get; init; } = [];
}

public sealed class StoryIntelligenceKnowledgeReviewCandidateViewModel
{
    public string Key { get; init; } = string.Empty;
    public string CharacterName { get; init; } = string.Empty;
    public string KnowledgeStatement { get; init; } = string.Empty;
    public int KnowledgeStateID { get; init; }
    public string KnowledgeStateName { get; init; } = "Knows";
    public int AppearsInScenes { get; init; }
    public string Confidence { get; init; } = "Unknown";
    public string? FirstScene { get; init; }
    public string? ExampleScene { get; init; }
    public string? Evidence { get; init; }
    public int? ExistingKnowledgeID { get; init; }
    public string? ExistingKnowledgeLabel { get; init; }
    public bool IsExistingMatch => ExistingKnowledgeID.HasValue;
}

public sealed class StoryIntelligenceKnowledgeImportForm
{
    public Guid BatchID { get; set; }
    public List<StoryIntelligenceKnowledgeImportChoiceForm> Knowledge { get; set; } = [];
}

public sealed class StoryIntelligenceKnowledgeImportChoiceForm
{
    public string Key { get; set; } = string.Empty;
    public string Action { get; set; } = StoryIntelligenceKnowledgeImportActions.CreateNew;
    public int? KnowledgeStateID { get; set; }
    public string? AliasTargetKey { get; set; }
}

public static class StoryIntelligenceKnowledgeImportActions
{
    public const string CreateNew = "CreateNew";
    public const string LinkExisting = "LinkExisting";
    public const string Ignore = "Ignore";
    public const string Alias = "Alias";
}

public sealed class StoryIntelligenceCompletionViewModel
{
    public StoryIntelligenceJobProgress Job { get; init; } = new();
    public Guid? BatchID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public int? FirstChapterID { get; init; }
    public string? ProjectName { get; init; }
    public string? BookTitle { get; init; }
    public int ChaptersCommitted { get; init; }
    public int ScenesCreated { get; init; }
    public int CharactersCreatedOrLinked { get; init; }
    public bool CharacterStageComplete { get; init; }
    public int LocationsCreatedOrLinked { get; init; }
    public bool LocationStageComplete { get; init; }
    public int AssetsCreatedOrLinked { get; init; }
    public bool AssetStageComplete { get; init; }
    public int RelationshipsCreatedOrLinked { get; init; }
    public bool RelationshipStageComplete { get; init; }
}

public sealed class StoryIntelligenceCharacterImportResultViewModel
{
    public Guid BatchID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public int CharactersCreated { get; init; }
    public int CharactersLinked { get; init; }
    public int CharactersAliased { get; init; }
    public int CharactersIgnored { get; init; }
    public int PovLinksResolved { get; init; }
    public int ScenePeoplePanelsUpdated { get; init; }
}

public sealed class StoryIntelligenceLocationImportResultViewModel
{
    public Guid BatchID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public int LocationsCreated { get; init; }
    public int LocationsLinked { get; init; }
    public int LocationAliasesAdded { get; init; }
    public int LocationsIgnored { get; init; }
    public int SceneLocationsUpdated { get; init; }
}

public sealed class StoryIntelligenceAssetImportResultViewModel
{
    public Guid BatchID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public int AssetsCreated { get; init; }
    public int AssetsLinked { get; init; }
    public int AssetAliasesAdded { get; init; }
    public int AssetsIgnored { get; init; }
    public int SceneAssetEventsCreated { get; init; }
    public int OwnershipLinksCreated { get; init; }
}

public sealed class StoryIntelligenceRelationshipImportResultViewModel
{
    public Guid BatchID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public int RelationshipsCreated { get; init; }
    public int RelationshipsLinked { get; init; }
    public int RelationshipsMerged { get; init; }
    public int RelationshipsIgnored { get; init; }
    public int RelationshipEventsCreated { get; init; }
}

public sealed class StoryIntelligenceKnowledgeImportResultViewModel
{
    public Guid BatchID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public int KnowledgeCreated { get; init; }
    public int KnowledgeLinked { get; init; }
    public int KnowledgeMerged { get; init; }
    public int KnowledgeIgnored { get; init; }
}

public sealed class StoryIntelligenceOnboardingChapterViewModel
{
    public string TemporaryChapterKey { get; init; } = string.Empty;
    public int ChapterNumber { get; init; }
    public string ChapterTitle { get; init; } = string.Empty;
    public int ChapterID { get; init; }
    public int RunID { get; init; }
    public string Status { get; init; } = string.Empty;
    public string? CurrentStage { get; init; }
    public string? CurrentMessage { get; init; }
    public int? TotalDetectedScenes { get; init; }
    public int? CompletedScenes { get; init; }
    public int? FailedScenes { get; init; }
    public int? SourceWordCount { get; init; }
    public int? TotalTokens { get; init; }
    public long? TotalDurationMs { get; init; }
    public DateTime? UpdatedUtc { get; init; }
    public decimal? EstimatedCostUSD { get; init; }
    public decimal? EstimatedCostGBP { get; init; }
    public string? FailureStage { get; init; }
    public string? ErrorMessage { get; init; }
    public string? LatestSceneSummary { get; init; }
    public bool HasCompletedCommit { get; init; }
    public bool CanCommit { get; init; }
    public int ScenesCreated { get; init; }
    public string? CommitMessage { get; init; }
    public IReadOnlyList<string> Blockers { get; init; } = [];
    public IReadOnlyList<string> Warnings { get; init; } = [];
    public IReadOnlyList<StoryIntelligenceOnboardingScenePreviewViewModel> Scenes { get; init; } = [];
    public decimal? AverageConfidence => Scenes.Count == 0 ? null : Scenes.Average(scene => scene.Confidence ?? 0m);
    public bool IsActive => Status is StoryIntelligenceRunStatuses.Pending or StoryIntelligenceRunStatuses.Running;
    public bool IsRunComplete => Status is StoryIntelligenceRunStatuses.Completed or StoryIntelligenceRunStatuses.CompletedWithWarnings;
    public bool IsFailed => Status is StoryIntelligenceRunStatuses.Failed or StoryIntelligenceRunStatuses.Cancelled;
}

public sealed class StoryIntelligenceOnboardingScenePreviewViewModel
{
    public int SceneNumber { get; init; }
    public string Summary { get; init; } = string.Empty;
    public string? Pov { get; init; }
    public string? Setting { get; init; }
    public decimal? Confidence { get; init; }
    public bool HasWarnings { get; init; }
}

public sealed class StoryIntelligenceDashboardViewModel
{
    public bool ShouldShow { get; init; }
    public string Title { get; init; } = "Story Intelligence available";
    public string Description { get; init; } = "Prepare the optional analysis framework for your manuscript.";
    public string ButtonText { get; init; } = "Set up Story Intelligence";
    public StoryIntelligenceJobProgress? Job { get; init; }
    public Guid? ActiveBatchID { get; init; }
    public string? ActiveImportBookTitle { get; init; }
    public string? ActiveImportProgressLabel { get; init; }
    public IReadOnlyList<StoryIntelligencePipelineBookStatusViewModel> PipelineBooks { get; init; } = [];
    public bool HasActiveImport => ActiveBatchID.HasValue;
    public bool HasPipelineBooks => PipelineBooks.Count > 0;
}

public sealed class StoryIntelligencePipelineBookStatusViewModel
{
    public int BookID { get; init; }
    public string BookTitle { get; init; } = string.Empty;
    public string StatusLabel { get; init; } = "Story Intelligence Needs Review";
    public string Description { get; init; } = string.Empty;
    public bool IsComplete { get; init; }
}

public sealed class CompanionPresenceViewModel
{
    public bool IsConnected { get; init; }
    public bool DocumentOpen { get; init; }
    public string Status { get; init; } = "Offline";
    public string? CompanionVersion { get; init; }
    public string? CurrentDocumentName { get; init; }
    public DateTime? LastHeartbeatUtc { get; init; }
}

public sealed class ManuscriptScanStateViewModel
{
    public string Status { get; init; } = ManuscriptScanStatuses.NotStarted;
    public string ReviewStatus { get; init; } = ManuscriptScanReviewStatuses.ScanComplete;
    public string Message { get; init; } = string.Empty;
    public int? PercentComplete { get; init; }
    public int ChapterCount { get; init; }
    public int SceneCount { get; init; }
    public int CharacterCandidateCount { get; init; }
    public int TotalWordCount { get; init; }
    public Guid? PreviewID { get; init; }
    public bool IsRunning => string.Equals(Status, ManuscriptScanStatuses.Running, StringComparison.Ordinal);
    public bool IsComplete => string.Equals(Status, ManuscriptScanStatuses.Complete, StringComparison.Ordinal);
    public bool IsFailed => string.Equals(Status, ManuscriptScanStatuses.Failed, StringComparison.Ordinal);
}

public sealed class ManuscriptScanReviewViewModel
{
    public Guid PreviewID { get; init; }
    public string ReviewStatus { get; init; } = ManuscriptScanReviewStatuses.ScanComplete;
    public string DocumentTitle { get; init; } = "Word manuscript";
    public string SelectedProjectName { get; init; } = string.Empty;
    public string SelectedBookTitle { get; init; } = string.Empty;
    public int TotalWordCount { get; init; }
    public int ChapterCount { get; init; }
    public int SceneCount { get; init; }
    public int CharacterCandidateCount { get; init; }
    public int SelectedChapterCount { get; init; }
    public int SelectedSceneCount { get; init; }
    public int SelectedCharacterCount { get; init; }
    public int SelectedWordCount { get; init; }
    public IReadOnlyList<ManuscriptScanReviewChapterViewModel> Chapters { get; init; } = [];
    public IReadOnlyList<ManuscriptScanReviewCharacterViewModel> CharacterCandidates { get; init; } = [];
    public IReadOnlyList<ManuscriptScanExistingCharacterOptionViewModel> ExistingCharacterOptions { get; init; } = [];
}

public sealed class ManuscriptScanReviewChapterViewModel
{
    public string TemporaryChapterKey { get; init; } = string.Empty;
    public int ChapterNumber { get; init; }
    public string Title { get; init; } = string.Empty;
    public string ReviewTitle { get; init; } = string.Empty;
    public bool Include { get; init; }
    public int WordCount { get; init; }
    public IReadOnlyList<ManuscriptScanReviewSceneViewModel> Scenes { get; init; } = [];
}

public sealed class ManuscriptScanReviewSceneViewModel
{
    public string TemporarySceneKey { get; init; } = string.Empty;
    public string TemporaryChapterKey { get; init; } = string.Empty;
    public int SceneNumberWithinChapter { get; init; }
    public string? Title { get; init; }
    public string? ReviewTitle { get; init; }
    public bool Include { get; init; }
    public int WordCount { get; init; }
    public string? OpeningTextPreview { get; init; }
}

public sealed class ManuscriptScanReviewCharacterViewModel
{
    public string TemporaryCharacterKey { get; init; } = string.Empty;
    public string Name { get; init; } = string.Empty;
    public string ReviewName { get; init; } = string.Empty;
    public bool Include { get; init; }
    public int MentionCount { get; init; }
    public int QualityScore { get; init; }
    public string Category { get; init; } = "PossibleCharacter";
    public string? Reason { get; init; }
    public int? ExistingCharacterID { get; init; }
    public string? ExistingCharacterName { get; init; }
    public bool IsExistingCharacterMatch => ExistingCharacterID.HasValue;
}

public sealed class ManuscriptScanExistingCharacterOptionViewModel
{
    public int CharacterID { get; init; }
    public string CharacterName { get; init; } = string.Empty;
}

public sealed class ManuscriptScanReviewForm
{
    public Guid PreviewID { get; set; }
    public List<ManuscriptScanChapterReviewFormItem> Chapters { get; set; } = [];
    public List<ManuscriptScanSceneReviewFormItem> Scenes { get; set; } = [];
    public List<ManuscriptScanCharacterReviewFormItem> Characters { get; set; } = [];
}

public sealed class ManuscriptScanChapterReviewFormItem
{
    public string TemporaryChapterKey { get; set; } = string.Empty;
    public bool Include { get; set; }
    public string Title { get; set; } = string.Empty;
    public int ChapterNumber { get; set; }
}

public sealed class ManuscriptScanSceneReviewFormItem
{
    public string TemporarySceneKey { get; set; } = string.Empty;
    public string TemporaryChapterKey { get; set; } = string.Empty;
    public bool Include { get; set; }
    public string? Title { get; set; }
    public int SceneNumberWithinChapter { get; set; }
    public bool AllowZeroWords { get; set; }
}

public sealed class ManuscriptScanCharacterReviewFormItem
{
    public string TemporaryCharacterKey { get; set; } = string.Empty;
    public bool Include { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Category { get; set; } = "PossibleCharacter";
    public int? ExistingCharacterID { get; set; }
}
