namespace PlotLine.Models;

public static class StoryIntelligenceJobStatuses
{
    public const string Pending = "Pending";
    public const string Preparing = "Preparing";
    public const string ReadingScenes = "ReadingScenes";
    public const string AnalysingCharacters = "AnalysingCharacters";
    public const string AnalysingRelationships = "AnalysingRelationships";
    public const string AnalysingLocations = "AnalysingLocations";
    public const string AnalysingAssets = "AnalysingAssets";
    public const string AnalysingTimeline = "AnalysingTimeline";
    public const string Finalising = "Finalising";
    public const string Completed = "Completed";
    public const string Cancelled = "Cancelled";
    public const string Failed = "Failed";
}

public sealed class StoryIntelligenceJob
{
    public Guid JobID { get; init; }
    public int ConsentID { get; init; }
    public int UserID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public string Status { get; init; } = StoryIntelligenceJobStatuses.Pending;
    public DateTime CreatedUtc { get; init; }
    public DateTime? StartedUtc { get; init; }
    public DateTime? CompletedUtc { get; init; }
    public DateTime? CancelledUtc { get; init; }
    public int ProgressPercent { get; init; }
    public string CurrentStage { get; init; } = StoryIntelligenceJobStatuses.Pending;
    public string CurrentMessage { get; init; } = "Story Intelligence is queued.";
    public string? EstimatedRemaining { get; init; }
    public string? ErrorMessage { get; init; }
    public DateTime UpdatedUtc { get; init; }
    public bool IsActive => Status is StoryIntelligenceJobStatuses.Pending
        or StoryIntelligenceJobStatuses.Preparing
        or StoryIntelligenceJobStatuses.ReadingScenes
        or StoryIntelligenceJobStatuses.AnalysingCharacters
        or StoryIntelligenceJobStatuses.AnalysingRelationships
        or StoryIntelligenceJobStatuses.AnalysingLocations
        or StoryIntelligenceJobStatuses.AnalysingAssets
        or StoryIntelligenceJobStatuses.AnalysingTimeline
        or StoryIntelligenceJobStatuses.Finalising;
    public bool IsCompleted => string.Equals(Status, StoryIntelligenceJobStatuses.Completed, StringComparison.Ordinal);
    public bool IsCancelled => string.Equals(Status, StoryIntelligenceJobStatuses.Cancelled, StringComparison.Ordinal);
}

public sealed class StoryIntelligenceJobProgress
{
    public Guid JobID { get; init; }
    public int UserID { get; init; }
    public int ProjectID { get; init; }
    public int BookID { get; init; }
    public string Status { get; init; } = StoryIntelligenceJobStatuses.Pending;
    public string CurrentStage { get; init; } = StoryIntelligenceJobStatuses.Pending;
    public string CurrentMessage { get; init; } = "Story Intelligence is queued.";
    public int ProgressPercent { get; init; }
    public string? EstimatedRemaining { get; init; }
    public string ElapsedTime { get; init; } = "0 min";
    public string? ErrorMessage { get; init; }
    public DateTime UpdatedUtc { get; init; }
    public bool IsActive { get; init; }
    public bool IsCompleted { get; init; }
}

public sealed class StoryIntelligenceRunProgressEvent
{
    public int RunID { get; init; }
    public int UserID { get; init; }
    public int? ProjectID { get; init; }
    public int? BookID { get; init; }
    public int? ChapterID { get; init; }
    public decimal? ChapterNumber { get; init; }
    public string Status { get; init; } = StoryIntelligenceRunStatuses.Pending;
    public string CurrentStage { get; init; } = string.Empty;
    public string CurrentMessage { get; init; } = string.Empty;
    public string EventType { get; init; } = string.Empty;
    public int? TotalDetectedScenes { get; init; }
    public int? CompletedScenes { get; init; }
    public int? FailedScenes { get; init; }
    public int? TotalTokens { get; init; }
    public long? TotalDurationMs { get; init; }
    public int? CurrentSceneNumber { get; init; }
    public string ElapsedTime { get; init; } = "0 sec";
    public string? EstimatedRemaining { get; init; }
    public string? LatestSceneSummary { get; init; }
    public IReadOnlyList<string> RecentDiscoveries { get; init; } = [];
    public string? ErrorMessage { get; init; }
    public DateTime UpdatedUtc { get; init; } = DateTime.UtcNow;
    public bool IsActive { get; init; }
    public bool IsCompleted { get; init; }
}

public sealed class StoryIntelligenceOptions
{
    public string ApiKey { get; init; } = string.Empty;
    public string Model { get; init; } = string.Empty;
    public string ChapterStructureModel { get; init; } = string.Empty;
    public string SceneIntelligenceModel { get; init; } = string.Empty;
    public string ImageGenerationModel { get; init; } = string.Empty;
    public string ImageGenerationSize { get; init; } = "1024x1024";
    public int MaxOutputTokens { get; init; } = 4000;
    public int? SceneIntelligenceMaxOutputTokens { get; init; }
    public int TimeoutSeconds { get; init; } = 120;

    public string EffectiveModel => FirstConfigured(Model);
    public string EffectiveChapterStructureModel => FirstConfigured(ChapterStructureModel, Model);
    public string EffectiveSceneIntelligenceModel => FirstConfigured(SceneIntelligenceModel, Model);

    public static string BuildModelSummary(string? chapterStructureModel, string? sceneIntelligenceModel)
        => $"Chapter={FirstConfigured(chapterStructureModel)}; Scene={FirstConfigured(sceneIntelligenceModel)}";

    public static StoryIntelligenceStageModels ParseModelSummary(
        string? modelSummary,
        string defaultChapterStructureModel,
        string defaultSceneIntelligenceModel)
    {
        var chapterModel = defaultChapterStructureModel;
        var sceneModel = defaultSceneIntelligenceModel;

        if (!string.IsNullOrWhiteSpace(modelSummary))
        {
            var segments = modelSummary.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            var matchedStage = false;
            foreach (var segment in segments)
            {
                var parts = segment.Split('=', 2, StringSplitOptions.TrimEntries);
                if (parts.Length != 2 || string.IsNullOrWhiteSpace(parts[1]))
                {
                    continue;
                }

                if (string.Equals(parts[0], "Chapter", StringComparison.OrdinalIgnoreCase))
                {
                    chapterModel = parts[1];
                    matchedStage = true;
                }
                else if (string.Equals(parts[0], "Scene", StringComparison.OrdinalIgnoreCase))
                {
                    sceneModel = parts[1];
                    matchedStage = true;
                }
            }

            if (!matchedStage && !string.IsNullOrWhiteSpace(modelSummary))
            {
                chapterModel = modelSummary.Trim();
                sceneModel = modelSummary.Trim();
            }
        }

        return new StoryIntelligenceStageModels(
            FirstConfigured(chapterModel, defaultChapterStructureModel),
            FirstConfigured(sceneModel, defaultSceneIntelligenceModel));
    }

    private static string FirstConfigured(params string?[] values)
        => values.FirstOrDefault(value => !string.IsNullOrWhiteSpace(value))?.Trim() ?? string.Empty;
}

public sealed record StoryIntelligenceStageModels(string ChapterStructureModel, string SceneIntelligenceModel);

public sealed class StoryIntelligencePricingOptions
{
    public decimal? InputPerMillionUsd { get; init; }
    public decimal? OutputPerMillionUsd { get; init; }
    public decimal? UsdToGbpRate { get; init; }
    public Dictionary<string, StoryIntelligenceModelPricingOptions> Models { get; init; } = new(StringComparer.OrdinalIgnoreCase);

    public StoryIntelligenceModelPricingOptions? GetPricingForModel(string? model)
    {
        if (!string.IsNullOrWhiteSpace(model)
            && Models.TryGetValue(model.Trim(), out var modelPricing)
            && (modelPricing.InputPerMillionUsd.HasValue || modelPricing.OutputPerMillionUsd.HasValue))
        {
            return modelPricing;
        }

        return InputPerMillionUsd.HasValue || OutputPerMillionUsd.HasValue
            ? new StoryIntelligenceModelPricingOptions
            {
                InputPerMillionUsd = InputPerMillionUsd,
                OutputPerMillionUsd = OutputPerMillionUsd
            }
            : null;
    }
}

public sealed class StoryIntelligenceModelPricingOptions
{
    public decimal? InputPerMillionUsd { get; init; }
    public decimal? OutputPerMillionUsd { get; init; }
}

public sealed class StoryIntelligenceRun
{
    public Guid RunId { get; init; } = Guid.NewGuid();
    public int ProjectId { get; init; }
    public int BookId { get; init; }
    public DateTime StartedUtc { get; init; } = DateTime.UtcNow;
    public DateTime? CompletedUtc { get; init; }
    public string Status { get; init; } = "Pending";
    public string PromptVersion { get; init; } = string.Empty;
    public string Model { get; init; } = string.Empty;
    public int? InputTokens { get; init; }
    public int? OutputTokens { get; init; }
    public decimal? EstimatedCost { get; init; }
    public bool Cancelled { get; init; }
    public string? ErrorMessage { get; init; }
}

public sealed class StoryIntelligenceClientResult
{
    public string RawResponseText { get; init; } = string.Empty;
    public string Model { get; init; } = string.Empty;
    public TimeSpan Duration { get; init; }
    public int RetryCount { get; init; }
    public int? InputTokens { get; init; }
    public int? OutputTokens { get; init; }
    public decimal? EstimatedCost { get; init; }
}
