using System.Text.Json;
using PlotLine.Models;
using PlotLine.Models.StoryIntelligence;

namespace PlotLine.Services;

public interface IChapterStructureValidator
{
    ValidationResult Validate(ChapterStructureModel? chapterStructure, int paragraphCount);
}

public sealed class ChapterStructureValidator : IChapterStructureValidator
{
    public ValidationResult Validate(ChapterStructureModel? chapterStructure, int paragraphCount)
    {
        var result = new ValidationResult();

        try
        {
            if (chapterStructure is null)
            {
                AddError(result, "$", "Chapter Structure JSON could not be deserialised.", "Return one valid Chapter Structure JSON object.");
                return result;
            }

            if (paragraphCount <= 0)
            {
                AddError(result, "chapterText", "Chapter text must contain at least one non-empty paragraph.", "Enter chapter text with one or more paragraphs.");
            }

            RequiredString(chapterStructure.SchemaVersion, "schemaVersion", result);
            if (!string.Equals(chapterStructure.SchemaVersion, "1.0", StringComparison.Ordinal))
            {
                AddError(result, "schemaVersion", "Schema version must be \"1.0\".", "Return schemaVersion as \"1.0\".");
            }

            RequiredString(chapterStructure.ChapterSummary, "chapterSummary", result);
            if (chapterStructure.SceneBoundaries is null)
            {
                AddError(result, "sceneBoundaries", "Scene boundaries array is required.", "Return sceneBoundaries as an array.");
            }
            else
            {
                ValidateBoundaries(chapterStructure.SceneBoundaries, paragraphCount, result);
            }

            Unknown(chapterStructure.ExtensionData, "$", result);

            if (result.IsValid)
            {
                AddInfo(result, "$", "Validation passed.", "No action needed.");
            }
        }
        catch (Exception ex)
        {
            AddError(result, "$", $"Validator failed internally: {ex.Message}", "Review validator implementation for this response shape.");
        }

        return result;
    }

    private static void ValidateBoundaries(List<ChapterSceneBoundary> boundaries, int paragraphCount, ValidationResult result)
    {
        if (boundaries.Count == 0)
        {
            AddError(result, "sceneBoundaries", "At least one scene boundary is required.", "Return one boundary covering the chapter if no scene split is supported.");
            return;
        }

        var expectedStart = 1;
        for (var i = 0; i < boundaries.Count; i++)
        {
            var boundary = boundaries[i];
            var path = $"sceneBoundaries[{i}]";
            var expectedSceneNumber = i + 1;

            if (!boundary.SceneNumber.HasValue)
            {
                AddError(result, $"{path}.sceneNumber", "Scene number is required.", $"Return sceneNumber {expectedSceneNumber}.");
            }
            else if (boundary.SceneNumber.Value != expectedSceneNumber)
            {
                AddError(result, $"{path}.sceneNumber", "Scene numbers must be sequential from 1.", $"Use sceneNumber {expectedSceneNumber} for this boundary.");
            }

            if (!boundary.StartParagraph.HasValue)
            {
                AddError(result, $"{path}.startParagraph", "Start paragraph is required.", "Return the first paragraph number for this boundary.");
            }

            if (!boundary.EndParagraph.HasValue)
            {
                AddError(result, $"{path}.endParagraph", "End paragraph is required.", "Return the last paragraph number for this boundary.");
            }

            if (boundary.StartParagraph.HasValue && boundary.EndParagraph.HasValue)
            {
                if (boundary.StartParagraph.Value > boundary.EndParagraph.Value)
                {
                    AddError(result, path, "Start paragraph cannot be after end paragraph.", "Use an ordered inclusive paragraph range.");
                }

                if (boundary.StartParagraph.Value != expectedStart)
                {
                    AddError(result, $"{path}.startParagraph", "Scene boundaries must not overlap or leave gaps.", $"Use startParagraph {expectedStart}.");
                }

                if (boundary.StartParagraph.Value < 1 || boundary.EndParagraph.Value < 1)
                {
                    AddError(result, path, "Paragraph numbers must be 1 or greater.", "Use paragraph numbers from the submitted chapter text.");
                }

                if (paragraphCount > 0 && boundary.EndParagraph.Value > paragraphCount)
                {
                    AddError(result, $"{path}.endParagraph", "End paragraph exceeds the submitted chapter paragraph count.", $"Use a value from 1 to {paragraphCount}.");
                }

                expectedStart = boundary.EndParagraph.Value + 1;
            }

            Confidence(boundary.Confidence, $"{path}.confidence", result);
            RequiredString(boundary.Reason, $"{path}.reason", result);
            Unknown(boundary.ExtensionData, path, result);
        }

        if (paragraphCount > 0 && expectedStart <= paragraphCount)
        {
            AddError(result, "sceneBoundaries", "Scene boundaries do not cover the full chapter.", $"Add a boundary ending at paragraph {paragraphCount}.");
        }
    }

    private static void Confidence(decimal? value, string path, ValidationResult result)
    {
        if (!value.HasValue)
        {
            AddError(result, path, "Confidence is required.", "Return a confidence value from 0.0 to 1.0.");
            return;
        }

        if (value < 0 || value > 1)
        {
            AddError(result, path, "Confidence must be from 0.0 to 1.0.", "Use the 0.0 to 1.0 confidence scale.");
        }
    }

    private static void RequiredString(string? value, string path, ValidationResult result)
    {
        if (value is null)
        {
            AddError(result, path, "Required string property is missing or null.", "Return a supported string value.");
        }
        else if (string.IsNullOrWhiteSpace(value))
        {
            AddError(result, path, "Required string property is empty.", "Return a non-empty string supported by the chapter structure.");
        }
    }

    private static void Unknown(Dictionary<string, JsonElement>? values, string path, ValidationResult result)
    {
        if (values is null || values.Count == 0)
        {
            return;
        }

        foreach (var propertyName in values.Keys.Order(StringComparer.Ordinal))
        {
            AddWarning(result, path == "$" ? propertyName : $"{path}.{propertyName}", "Unknown property is not part of Chapter Structure V1.", "Remove this property or update the schema version intentionally.");
        }
    }

    private static void AddError(ValidationResult result, string path, string message, string suggestedFix)
        => result.Errors.Add(new ValidationIssue { Severity = "Error", Path = path, Message = message, SuggestedFix = suggestedFix });

    private static void AddWarning(ValidationResult result, string path, string message, string suggestedFix)
        => result.Warnings.Add(new ValidationIssue { Severity = "Warning", Path = path, Message = message, SuggestedFix = suggestedFix });

    private static void AddInfo(ValidationResult result, string path, string message, string suggestedFix)
        => result.Information.Add(new ValidationIssue { Severity = "Information", Path = path, Message = message, SuggestedFix = suggestedFix });
}
