using System.Text.Json.Serialization;

namespace PlotLine.Models;

public static class IllustrationLibraryCategories
{
    public const string Character = "Character";
    public const string Location = "Location";
    public const string Asset = "Asset";

    public static readonly IReadOnlyList<string> All = [Character, Location, Asset];

    public static bool IsValid(string? category)
        => All.Any(item => string.Equals(item, category, StringComparison.OrdinalIgnoreCase));

    public static string StorageFolder(string category)
        => category switch
        {
            Character => "characters",
            Location => "locations",
            Asset => "assets",
            _ => throw new ArgumentOutOfRangeException(nameof(category), category, "Unknown illustration category.")
        };
}

public static class IllustrationLibraryStatuses
{
    public const string Planned = "Planned";
    public const string Queued = "Queued";
    public const string Generating = "Generating";
    public const string Generated = "Generated";
    public const string Approved = "Approved";
    public const string Rejected = "Rejected";
    public const string Failed = "Failed";
    public const string Superseded = "Superseded";
    public const string Disabled = "Disabled";

    public static readonly IReadOnlySet<string> All = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
    {
        Planned,
        Queued,
        Generating,
        Generated,
        Approved,
        Rejected,
        Failed,
        Superseded,
        Disabled
    };

    public static bool IsValid(string? status)
        => !string.IsNullOrWhiteSpace(status) && All.Contains(status);

    public static bool CanTransition(string from, string to)
        => (from, to) switch
        {
            (Planned, Queued) => true,
            (Queued, Generating) => true,
            (Generating, Generated) => true,
            (Generating, Failed) => true,
            (Generated, Approved) => true,
            (Generated, Rejected) => true,
            (Approved, Superseded) => true,
            (Rejected, Queued) => true,
            (Failed, Queued) => true,
            (Approved, Disabled) => true,
            (Disabled, Queued) => true,
            _ when string.Equals(from, to, StringComparison.OrdinalIgnoreCase) => true,
            _ => false
        };
}

public sealed class IllustrationGenerationSpecification
{
    public string Category { get; init; } = string.Empty;
    public string Code { get; init; } = string.Empty;
    public string Title { get; init; } = string.Empty;
    public string Description { get; init; } = string.Empty;
    public string VisualSubject { get; init; } = string.Empty;
    public string Composition { get; init; } = string.Empty;
    public string Mood { get; init; } = string.Empty;
    public string? ApparentAgeBand { get; init; }
    public string? Presentation { get; init; }
    public string? HairColour { get; init; }
    public string? HairLength { get; init; }
    public string? SkinTone { get; init; }
    public string? ClothingEra { get; init; }
    public string? ClothingStyle { get; init; }
    public string? LocationType { get; init; }
    public string? Era { get; init; }
    public string? Setting { get; init; }
    public string? ObjectType { get; init; }
    public string? Colour { get; init; }
    public IReadOnlyList<string> Features { get; init; } = [];
    public IReadOnlyList<string> Palette { get; init; } = [];
    public IReadOnlyList<string> MetadataTags { get; init; } = [];
    public Dictionary<string, string> Metadata { get; init; } = new(StringComparer.OrdinalIgnoreCase);

    public IReadOnlyList<string> Validate()
    {
        var errors = new List<string>();
        if (!IllustrationLibraryCategories.IsValid(Category))
        {
            errors.Add("Category must be Character, Location or Asset.");
        }

        if (string.IsNullOrWhiteSpace(Code))
        {
            errors.Add("Stable code is required.");
        }

        if (string.IsNullOrWhiteSpace(Title))
        {
            errors.Add("Title is required.");
        }

        if (string.IsNullOrWhiteSpace(Mood))
        {
            errors.Add("Mood is required.");
        }

        if (Category == IllustrationLibraryCategories.Character)
        {
            Require(ApparentAgeBand, "Apparent age band is required for characters.");
            Require(Presentation, "Presentation is required for characters.");
        }
        else if (Category == IllustrationLibraryCategories.Location)
        {
            Require(LocationType, "Location type is required for locations.");
        }
        else if (Category == IllustrationLibraryCategories.Asset)
        {
            Require(ObjectType, "Object type is required for assets.");
        }

        return errors;

        void Require(string? value, string message)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                errors.Add(message);
            }
        }
    }
}

public sealed class IllustrationLibraryItem
{
    public int IllustrationLibraryItemID { get; set; }
    public string Category { get; set; } = string.Empty;
    public string StableCode { get; set; } = string.Empty;
    public string DisplayTitle { get; set; } = string.Empty;
    public string? ShortDescription { get; set; }
    public string? MetadataJson { get; set; }
    public string? SpecificationJson { get; set; }
    public string? FilePath { get; set; }
    public string? ThumbnailPath { get; set; }
    public int? Width { get; set; }
    public int? Height { get; set; }
    public long? FileSizeBytes { get; set; }
    public string? MimeType { get; set; }
    public string Status { get; set; } = IllustrationLibraryStatuses.Planned;
    public bool IsActive { get; set; }
    public bool IsApproved { get; set; }
    public DateTime CreatedUtc { get; set; }
    public DateTime UpdatedUtc { get; set; }
    public DateTime? ApprovedUtc { get; set; }
    public int? ApprovedByUserID { get; set; }
    public string? Provider { get; set; }
    public string? Model { get; set; }
    public string? PromptText { get; set; }
    public string? Prompt { get; set; }
    public string? PromptTemplateVersion { get; set; }
    public string? ProviderRequestID { get; set; }
    public int AttemptCount { get; set; }
    public int GenerationAttemptCount { get; set; }
    public int? ParentItemID { get; set; }
    public string? RejectionReason { get; set; }
    public DateTime? RejectedUtc { get; set; }
    public int? RejectedByUserID { get; set; }
    public string? AdminNotes { get; set; }
    public int UsageCount { get; set; }
    public string? LastError { get; set; }
    public string? ErrorMessage { get; set; }
    public DateTime? QueuedUtc { get; set; }
    public DateTime? StartedUtc { get; set; }
    public DateTime? CompletedUtc { get; set; }
}

public sealed class IllustrationLibrarySummary
{
    public int TotalCount { get; set; }
    public int PlannedCount { get; set; }
    public int QueuedCount { get; set; }
    public int GeneratingCount { get; set; }
    public int GeneratedCount { get; set; }
    public int ApprovedCount { get; set; }
    public int RejectedCount { get; set; }
    public int FailedCount { get; set; }
    public int SupersededCount { get; set; }
    public int DisabledCount { get; set; }
}

public sealed class IllustrationLibraryFilter
{
    public string? Category { get; init; }
    public string? Status { get; init; }
    public string? Search { get; init; }
}

public sealed record IllustrationPromptBuildResult(
    string Prompt,
    string TemplateVersion,
    string SpecificationJson,
    string MetadataJson);

public sealed record IllustrationImageGenerationRequest(
    int IllustrationLibraryItemID,
    string StableCode,
    string Category,
    string Prompt,
    string TemplateVersion);

public sealed record IllustrationImageGenerationResult(
    bool Succeeded,
    byte[]? ImageBytes = null,
    string? MimeType = null,
    string? Provider = null,
    string? Model = null,
    string? ProviderRequestID = null,
    string? ErrorMessage = null)
{
    [JsonIgnore]
    public bool HasImage => ImageBytes is { Length: > 0 };
}

public sealed record IllustrationStoredImage(
    string FilePath,
    string ThumbnailPath,
    int Width,
    int Height,
    long FileSizeBytes,
    string MimeType);

public sealed record IllustrationLibraryActionResult(bool Succeeded, string Message);

public sealed record IllustrationBulkRetryResult(
    int QueuedCount,
    int SkippedCount,
    int RetryLimitReachedCount)
{
    public string Message
        => $"{QueuedCount:N0} failed illustration(s) queued. {SkippedCount:N0} skipped. {RetryLimitReachedCount:N0} reached the retry limit.";
}

public sealed record IllustrationStarterGenerationPlan(
    int CharactersToGenerate,
    int LocationsToGenerate,
    int AssetsToGenerate)
{
    public int TotalToGenerate => CharactersToGenerate + LocationsToGenerate + AssetsToGenerate;
}
