namespace YMhut.Box.Core.Tools;

public sealed record ToolResultExperience(
    string ToolId,
    string ExperienceId,
    string Layout,
    string Accent,
    string Tone,
    string PrimaryPrimitive,
    IReadOnlyList<string> SecondaryPrimitives);

public sealed record ToolPageExperience(
    string ToolId,
    string ExperienceId,
    string InputLayout,
    string ResultLayout,
    string Accent,
    string Tone,
    IReadOnlyList<string> InputPrimitives,
    IReadOnlyList<string> ResultPrimitives,
    IReadOnlyList<string> Actions);

public interface IToolResultExperienceCatalog
{
    ToolResultExperience GetRequired(string toolId);

    ToolPageExperience GetRequiredPage(string toolId);

    IReadOnlyDictionary<string, ToolResultExperience> Experiences { get; }

    IReadOnlyDictionary<string, ToolPageExperience> PageExperiences { get; }
}

public sealed class ToolResultExperienceCatalog : IToolResultExperienceCatalog
{
    private readonly IReadOnlyDictionary<string, ToolResultExperience> _experiences;
    private readonly IReadOnlyDictionary<string, ToolPageExperience> _pageExperiences;

    public ToolResultExperienceCatalog(ToolCatalog catalog)
    {
        _experiences = catalog.Modules.ToDictionary(module => module.Id, CreateExperience, StringComparer.OrdinalIgnoreCase);
        _pageExperiences = catalog.Modules.ToDictionary(module => module.Id, CreatePageExperience, StringComparer.OrdinalIgnoreCase);
    }

    public IReadOnlyDictionary<string, ToolResultExperience> Experiences => _experiences;

    public IReadOnlyDictionary<string, ToolPageExperience> PageExperiences => _pageExperiences;

    public ToolResultExperience GetRequired(string toolId)
    {
        if (_experiences.TryGetValue(toolId, out var experience))
        {
            return experience;
        }

        throw new InvalidOperationException($"Tool result experience is missing for '{toolId}'.");
    }

    public ToolPageExperience GetRequiredPage(string toolId)
    {
        if (_pageExperiences.TryGetValue(toolId, out var experience))
        {
            return experience;
        }

        throw new InvalidOperationException($"Tool page experience is missing for '{toolId}'.");
    }

    public static ToolResultExperience CreateExperience(IToolModule module)
    {
        var spec = ToolPageSpecCatalog.For(module);
        var slug = module.Id.Replace('_', '-').ToLowerInvariant();
        var family = ResolveFamily(module, spec);
        var primitive = ResolvePrimaryPrimitive(module, spec);
        return new ToolResultExperience(
            module.Id,
            $"{slug}-{family}-{primitive}",
            $"{family}-{spec.Result.ToString().ToLowerInvariant()}",
            AccentFor(module.Metadata.Category),
            ToneFor(module, spec),
            primitive,
            SecondaryPrimitives(spec));
    }

    public static ToolPageExperience CreatePageExperience(IToolModule module)
    {
        var spec = ToolPageSpecCatalog.For(module);
        var result = CreateExperience(module);
        var inputLayout = InputLayoutFor(spec);
        var resultLayout = $"{ResolveFamily(module, spec)}-{result.PrimaryPrimitive}";
        var slug = module.Id.Replace('_', '-').ToLowerInvariant();
        return new ToolPageExperience(
            module.Id,
            $"{slug}-{inputLayout}-{result.PrimaryPrimitive}",
            inputLayout,
            resultLayout,
            result.Accent,
            result.Tone,
            InputPrimitives(spec),
            [result.PrimaryPrimitive, ..result.SecondaryPrimitives],
            PageActions(spec));
    }

    private static string ResolveFamily(IToolModule module, ToolPageSpec spec)
    {
        if (module.Id.Contains("json", StringComparison.OrdinalIgnoreCase) || module.Id is "jwt_decoder")
        {
            return "structured";
        }

        if (spec.Layout is ToolLayoutKind.CalculatorForm or ToolLayoutKind.UnitConverter)
        {
            return "calculation";
        }

        if (spec.Layout is ToolLayoutKind.QueryDashboard or ToolLayoutKind.Dashboard)
        {
            return module.Metadata.OfflineCapable ? "reference" : "live";
        }

        if (spec.Layout is ToolLayoutKind.FileWorkflow)
        {
            return "fileflow";
        }

        if (spec.Layout is ToolLayoutKind.SecurityWorkbench)
        {
            return "security";
        }

        if (spec.Result is ToolResultKind.CodePreview or ToolResultKind.JsonTree)
        {
            return "code";
        }

        return module.Metadata.Category.ToString().ToLowerInvariant();
    }

    private static string ResolvePrimaryPrimitive(IToolModule module, ToolPageSpec spec)
    {
        return spec.Result switch
        {
            ToolResultKind.JsonTree => "json-tree",
            ToolResultKind.CodePreview => "code-panel",
            ToolResultKind.Table or ToolResultKind.CalculatorTable or ToolResultKind.ReferenceRows => "data-table",
            ToolResultKind.RankedList => "ranked-lane",
            ToolResultKind.NewsCards => "news-board",
            ToolResultKind.ImagePreview => "media-stage",
            ToolResultKind.LinkCards => "link-dock",
            ToolResultKind.FileCards => "file-rail",
            ToolResultKind.Diff => "diff-stack",
            ToolResultKind.StatusList => "status-timeline",
            ToolResultKind.ColorSwatch => "color-lab",
            ToolResultKind.SystemCards => "metric-grid",
            ToolResultKind.Media => "media-wall",
            _ when module.Id.Contains("generator", StringComparison.OrdinalIgnoreCase) => "generator-strip",
            _ => "text-stream"
        };
    }

    private static IReadOnlyList<string> SecondaryPrimitives(ToolPageSpec spec)
    {
        var primitives = new List<string> { "summary-metrics", "raw-drawer" };
        if (spec.UseVirtualizedResults)
        {
            primitives.Add("virtual-list");
        }
        if (spec.ResultPriority is ToolResultPriority.Table)
        {
            primitives.Add("column-scan");
        }
        if (spec.ResultPriority is ToolResultPriority.Media or ToolResultPriority.Preview)
        {
            primitives.Add("preview-actions");
        }
        return primitives;
    }

    private static string InputLayoutFor(ToolPageSpec spec)
    {
        return spec.PrimaryInput switch
        {
            ToolPrimaryInputKind.None => spec.AutoRunOnOpen ? "auto-dashboard" : "action-panel",
            ToolPrimaryInputKind.MultilineText => "editor-workbench",
            ToolPrimaryInputKind.Query or ToolPrimaryInputKind.Url => "search-console",
            ToolPrimaryInputKind.Token => "secret-entry",
            ToolPrimaryInputKind.Color => "color-studio",
            ToolPrimaryInputKind.FixedOptions => "preset-browser",
            ToolPrimaryInputKind.FilePath => "file-dropzone",
            ToolPrimaryInputKind.DateRange => "date-range-form",
            ToolPrimaryInputKind.Number or ToolPrimaryInputKind.NumberPair or ToolPrimaryInputKind.NumberTriple or ToolPrimaryInputKind.NumberQuad => "numeric-calculator",
            _ => "text-console"
        };
    }

    private static IReadOnlyList<string> InputPrimitives(ToolPageSpec spec)
    {
        var primitives = new List<string>();
        if (spec.PrimaryInput is ToolPrimaryInputKind.Text or ToolPrimaryInputKind.MultilineText or ToolPrimaryInputKind.Query or ToolPrimaryInputKind.Url or ToolPrimaryInputKind.Token)
        {
            primitives.Add("text-input");
        }
        if (spec.PrimaryInput == ToolPrimaryInputKind.MultilineText)
        {
            primitives.Add("monospace-editor");
        }
        if (spec.PrimaryInput == ToolPrimaryInputKind.FilePath || spec.RequiresFilePicker)
        {
            primitives.Add("file-picker");
        }
        if (spec.UsesNumberBox || spec.PrimaryInput is ToolPrimaryInputKind.Number or ToolPrimaryInputKind.NumberPair or ToolPrimaryInputKind.NumberTriple or ToolPrimaryInputKind.NumberQuad)
        {
            primitives.Add("number-grid");
        }
        if (spec.UsesDatePicker || spec.PrimaryInput == ToolPrimaryInputKind.DateRange)
        {
            primitives.Add("date-fields");
        }
        if (spec.Rules.Count > 0)
        {
            primitives.Add("rule-strip");
        }
        if (spec.Parameters.Count > 0 || spec.UsesComboBox)
        {
            primitives.Add("preset-select");
        }
        if (primitives.Count == 0)
        {
            primitives.Add("action-surface");
        }

        return primitives;
    }

    private static IReadOnlyList<string> PageActions(ToolPageSpec spec)
    {
        var actions = new List<string>();
        if (!string.IsNullOrWhiteSpace(spec.PrimaryActionLabel))
        {
            actions.Add("run");
        }
        if (spec.RequiresFilePicker || spec.SecondaryActions.Contains("chooseFile"))
        {
            actions.Add("chooseFile");
        }
        if (spec.SecondaryActions.Contains("clear"))
        {
            actions.Add("clear");
        }
        actions.Add("copyResult");
        actions.Add("showRaw");
        return actions.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
    }

    private static string AccentFor(ToolCategory category)
    {
        return category switch
        {
            ToolCategory.Network => "#38BDF8",
            ToolCategory.Security => "#F97316",
            ToolCategory.Data => "#A78BFA",
            ToolCategory.Calculator => "#22C55E",
            ToolCategory.Text => "#FACC15",
            ToolCategory.Image => "#EC4899",
            ToolCategory.Design => "#14B8A6",
            ToolCategory.Life => "#84CC16",
            ToolCategory.System => "#60A5FA",
            ToolCategory.Plugin => "#C084FC",
            _ => "#22C55E"
        };
    }

    private static string ToneFor(IToolModule module, ToolPageSpec spec)
    {
        if (!module.Metadata.OfflineCapable)
        {
            return "live-network";
        }

        return spec.PageExperience switch
        {
            ToolPageExperienceKind.FormCalculator => "precision",
            ToolPageExperienceKind.FileWorkflow => "workflow",
            ToolPageExperienceKind.LookupBrowser => "reference",
            ToolPageExperienceKind.LivePreview => "studio",
            ToolPageExperienceKind.SystemLauncher => "system",
            _ => "local"
        };
    }
}

public sealed record ToolResultWebPayload(
    string ToolId,
    string ToolName,
    ToolResultDocument ResultDocument,
    string ExperienceId,
    string Theme,
    string Language,
    ToolResultPrivacyPolicy PrivacyPolicy,
    ToolResultRuntimeMetadata RuntimeMetadata,
    ToolResultExperience Experience);

public sealed record ToolPageWebPayload(
    string ToolId,
    string ToolName,
    string ToolDescription,
    ToolPageSpec Spec,
    ToolPageExperience Experience,
    ToolInputState Input,
    string Theme,
    string Language,
    ToolResultPrivacyPolicy PrivacyPolicy,
    ToolPageRuntimeMetadata RuntimeMetadata,
    IReadOnlyList<string> AvailableActions,
    ToolResultDocument? Result = null,
    ToolResultRunState? RunState = null);

public sealed record ToolInputState(
    string Text,
    IReadOnlyList<ToolInputField> Fields,
    IReadOnlyDictionary<string, string> Rules,
    IReadOnlyDictionary<string, string> Metadata);

public sealed record ToolInputField(
    string Key,
    string Label,
    string Kind,
    string Value,
    string Placeholder = "",
    double? Minimum = null,
    double? Maximum = null,
    IReadOnlyList<ToolInputOption>? Options = null);

public sealed record ToolInputOption(string Label, string Value, string Description = "");

public sealed record ToolResultRunState(
    string State,
    string Message,
    string? Error = null,
    long DurationMs = 0);

public sealed record ToolPageRuntimeMetadata(
    DateTimeOffset CreatedAt,
    bool AutoRun,
    bool OfflineCapable,
    string Category,
    string Icon,
    string Source = "tool-page");

public sealed record ToolResultPrivacyPolicy(
    IReadOnlyList<string> RedactedHostHints,
    string Mode = "ymhut-api-only");

public sealed record ToolResultRuntimeMetadata(
    long DurationMs,
    DateTimeOffset CompletedAt,
    bool Cached,
    string Source,
    IReadOnlyDictionary<string, string> ToolMetadata);
