using System.Collections.Generic;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using HellionChat._Helpers;

namespace HellionChat.Util;

// UI-7: produces a render-only view of a chunk list with the sender's name
// reformatted per the user's WorldSuffixMode / NameFormMode. Called from
// ChatLogWindow.DrawChunks on every draw — it never mutates the input, so the
// stored message (the Sender BLOB in the DB) stays byte-for-byte unchanged and
// a later settings change reformats history too.
// Known trade-off: at non-default settings, this allocates one List<Chunk> per
// visible message per frame. Lists are small and the path is skipped at the
// neutral defaults, so GC pressure is low in practice. Accepted; noted in
// Cycle Notes.
internal static class SenderNameDisplay
{
    // Returns a copy of the list with the whole ChunkSource.Sender span
    // collapsed to one formatted chunk, or the original list when nothing
    // should change (neutral defaults, no sender span, or a non-player sender).
    internal static IReadOnlyList<Chunk> ForDisplay(IReadOnlyList<Chunk> chunks)
    {
        var suffixMode = Plugin.Config.WorldSuffixMode;
        var formMode = Plugin.Config.NameFormMode;

        // Neutral default = today's rendering. Bail before scanning so the
        // common case stays free.
        if (suffixMode == WorldSuffixMode.OtherWorldOnly && formMode == NameFormMode.Full)
            return chunks;

        // Locate the sender span. A content list has no Sender chunks and is
        // returned untouched.
        var first = -1;
        var last = -1;
        for (var i = 0; i < chunks.Count; i++)
        {
            if (chunks[i].Source != ChunkSource.Sender)
                continue;
            if (first < 0)
                first = i;
            last = i;
        }
        if (first < 0)
            return chunks;

        // The PlayerPayload rides on the sender chunks; a system or non-player
        // sender has none and is left alone.
        PlayerPayload? payload = null;
        for (var i = first; i <= last; i++)
        {
            if (chunks[i].Link is PlayerPayload pp)
            {
                payload = pp;
                break;
            }
        }
        if (payload is null)
            return chunks;

        var worldName = payload.World.ValueNullable?.Name.ExtractText() ?? string.Empty;

        // IPlayerState.HomeWorld reads AgentLobby directly without a
        // framework-thread guard (reference_dalamud_framework_thread), so this
        // call is safe from the draw thread.
        // RowId is a value field — safe to read directly without ValueNullable, unlike the world name above.
        var isHomeWorld = payload.World.RowId == Plugin.PlayerState.HomeWorld.RowId;

        var formatted = SenderNameFormatter.Format(
            payload.PlayerName,
            worldName,
            isHomeWorld,
            formMode,
            suffixMode
        );

        // Render-only copy: replace the whole sender span (name text, world
        // text, and any cross-world icon) with one formatted chunk that keeps
        // the PlayerPayload link so the name stays clickable. Dropping the
        // original sender icon is an accepted trade-off and only happens once
        // the user moves UI-7 off its defaults.
        // Channel brackets and colons are ChunkSource.None wrappers outside the
        // Sender span — they are preserved untouched by the copy loops below.
        var copy = new List<Chunk>(chunks.Count);
        for (var i = 0; i < first; i++)
            copy.Add(chunks[i]);
        TextChunk? styleSource = null;
        for (var i = first; i <= last; i++)
        {
            if (chunks[i] is TextChunk tc)
            {
                styleSource = tc;
                break;
            }
        }
        var replacement = styleSource is not null
            ? styleSource.NewWithStyle(ChunkSource.Sender, payload, formatted)
            : new TextChunk(ChunkSource.Sender, payload, formatted);
        copy.Add(replacement);
        for (var i = last + 1; i < chunks.Count; i++)
            copy.Add(chunks[i]);
        return copy;
    }
}
