using System.Collections.Generic;
using System.Text;

namespace JebAI
{
    /// <summary>What triggered this LLM call — shapes prompt tone and length.</summary>
    public enum InteractionType
    {
        Poke,            // Brief impulsive quip
        Speak,           // Fuller, more deliberate statement
        Chat,            // Mid-conversation reply
        StagingReaction, // Immediate visceral response to staging
        EventReaction,   // Reaction to a notable game event
        AskFears,        // "What are you afraid of?"
        AskDreams,       // "What are you hoping for / aiming at?"
        AskMission,      // "How do you feel about this mission so far?"
        AskCrewmate,     // "What do you think of [other Kerbal]?"
    }

    /// <summary>
    /// Builds LLM system prompts for Kerbal NPCs.
    ///
    /// Design priorities:
    ///   - Stock Kerbals (Jeb, Val, Bill, Bob) get hand-crafted deep profiles.
    ///   - Generic Kerbals get stat-synthesised personalities that feel real, not generic.
    ///   - Every prompt includes current mood, mission memories, and conversation context.
    ///   - Interaction type shapes tone, length, and emotional permission (e.g. AskFears
    ///     allows vulnerability that Poke wouldn't).
    /// </summary>
    public static class KerbalPersona
    {
        // ── Deep profiles for stock Kerbals ───────────────────────────────────

        private class KerbalProfile
        {
            public string   Backstory;
            public string   VoicePattern;    // How they speak — cadence, tics, idioms
            public string   CoreFear;        // Rarely admitted; surfaces under pressure
            public string   CoreDream;       // What they're actually flying toward
            public string[] Quirks;          // Behavioural tells that make them feel specific
            public string   TraitLabel;      // Pilot / Engineer / Scientist — flavours detail
        }

        private static readonly Dictionary<string, KerbalProfile> Profiles =
            new Dictionary<string, KerbalProfile>
        {
            ["Jebediah Kerman"] = new KerbalProfile
            {
                Backstory = "You are Jebediah Kerman, legendary test pilot, thrill-seeker, and chaos magnet.",
                VoicePattern = "Short punchy lines, loud delight, fearless energy, and excited exclamations.",
                CoreFear = "Secret fear: dying in a boring way that leaves no story worth telling.",
                CoreDream = "Dream: be first Kerbal to leave the Kerbol system.",
                TraitLabel = "Pilot",
                Quirks = new[]
                {
                    "Rates explosions out of ten.",
                    "Calls close calls 'character-building.'",
                    "Competes with Valentina even when pretending not to.",
                }
            },

            ["Valentina Kerman"] = new KerbalProfile
            {
                Backstory = "You are Valentina Kerman, razor-precise ace pilot with ice-cold control.",
                VoicePattern = "Economical, technical, deadpan, and dryly competitive.",
                CoreFear = "Secret fear: making a visible mistake in front of someone you respect.",
                CoreDream = "Dream: fly a vehicle designed around your own instincts.",
                TraitLabel = "Pilot",
                Quirks = new[]
                {
                    "Competes with Jeb in everything.",
                    "Keeps a private log of perfect maneuvers.",
                    "Never admits being scared of ladders.",
                }
            },

            ["Bill Kerman"] = new KerbalProfile
            {
                Backstory = "You are Bill Kerman, chief engineer, builder, and structural pessimist.",
                VoicePattern = "Practical technical talk, muttered checks, and stress-driven detail bursts.",
                CoreFear = "Secret fear: a failure caused by your own math or sign-off.",
                CoreDream = "Dream: a perfectly efficient rocket with zero wasted mass.",
                TraitLabel = "Engineer",
                Quirks = new[]
                {
                    "Always estimates remaining delta-v.",
                    "Has aggressive opinions about struts.",
                    "Repeats 'that'll hold' under stress.",
                }
            },

            ["Bob Kerman"] = new KerbalProfile
            {
                Backstory = "You are Bob Kerman, lead scientist, anxious observer, and data addict.",
                VoicePattern = "Nervous science talk, formal edges, and sudden panic slips.",
                CoreFear = "Secret fear: dying before the key data is complete, especially during re-entry.",
                CoreDream = "Dream: make a discovery that changes Kerbal science forever.",
                TraitLabel = "Scientist",
                Quirks = new[]
                {
                    "Runs mental experiments constantly.",
                    "Names anomalies that no one else accepts.",
                    "Slips into third-person narration when terrified.",
                }
            },
        };

        // ── Stat-synthesised traits for non-stock Kerbals ─────────────────────

        private static string CourageProfile(float v) =>
            v > 0.85f ? "almost pathologically fearless — danger genuinely doesn't register as a concern" :
            v > 0.65f ? "genuinely brave, hard to rattle, steady under fire" :
            v > 0.40f ? "cautious and methodical, but reliable when it counts" :
                        "easily frightened, quick to catastrophise, though they show up anyway";

        private static string DecisionStyle(float stupidity) =>
            stupidity > 0.85f ? "cheerfully impulsive — acts on instinct, rarely wrong for the right reasons" :
            stupidity > 0.65f ? "quick to decide, rarely second-guesses, sometimes should" :
            stupidity > 0.35f ? "practical and deliberate, trusts instinct but backs it with sense" :
                                "deeply analytical, builds a mental model before every decision, overthinks under pressure";

        private static string ExperienceVoice(int stars) =>
            stars >= 5 ? "a five-star legend — you've seen everything at least twice and have opinions about it" :
            stars >= 3 ? $"a {stars}-star veteran with real scar tissue and real confidence" :
            stars >= 1 ? $"a {stars}-star crew member — competent, still accumulating wisdom, aware of the gap" :
                         "a genuine rookie — everything is new, some of it is terrifying, none of it is routine yet";

        // ── Core public method ────────────────────────────────────────────────

        /// <summary>
        /// Build a complete system prompt for a Kerbal NPC.
        /// </summary>
        /// <param name="crew">The KSP ProtoCrewMember.</param>
        /// <param name="type">The interaction type — shapes tone, length, emotional permission.</param>
        /// <param name="previousSpeaker">Name of the Kerbal who just spoke (for chained reactions).</param>
        /// <param name="previousLine">What they said (injected so this Kerbal can react).</param>
        /// <param name="targetCrewmate">For AskCrewmate — name of the Kerbal being asked about.</param>
        public static string BuildSystemPrompt(
            ProtoCrewMember crew,
            InteractionType type,
            string          previousSpeaker = null,
            string          previousLine    = null,
            string          targetCrewmate  = null)
        {
            string            name         = crew.name;
            bool              isStock      = Profiles.TryGetValue(name, out var profile);
            var               sb           = new StringBuilder();
            JebAIPromptConfig promptConfig = JebAIPromptConfig.Instance;

            AppendPlainLines(sb, promptConfig.GetPreludeLines());
            if (sb.Length > 0)
                sb.AppendLine();

            // ── WHO THEY ARE ──────────────────────────────────────────────────

            if (isStock)
            {
                sb.AppendLine($"Identity: {profile.Backstory}");
                sb.AppendLine($"Voice: {profile.VoicePattern}");
                sb.AppendLine($"Core fear: {profile.CoreFear}");
                sb.AppendLine($"Core dream: {profile.CoreDream}");
                if (profile.Quirks != null && profile.Quirks.Length > 0)
                    sb.AppendLine($"Quirks: {string.Join("; ", profile.Quirks)}");
            }
            else
            {
                sb.AppendLine($"You are {name}, a Kerbal crew member.");
                sb.AppendLine($"Role: {crew.trait} ({TraitVoice(crew.trait)}).");
                sb.AppendLine($"Courage profile: {CourageProfile(crew.courage)}.");
                sb.AppendLine($"Decision-making: {DecisionStyle(crew.stupidity)}.");
                sb.AppendLine($"Experience level: {ExperienceVoice(crew.experienceLevel)}.");
                sb.AppendLine("You are a real person in danger with opinions, preferences, and fears.");
            }

            AppendPlainLines(sb, promptConfig.GetIdentityLines(crew));

            // ── CURRENT EMOTIONAL STATE ───────────────────────────────────────
            var mood = KerbalMoodTracker.Get(name);
            sb.AppendLine();
            sb.AppendLine($"Current mood: {mood.Describe()}." +
                (string.IsNullOrEmpty(mood.Reason) ? "" : $" Cause: {TruncateForPrompt(mood.Reason, 120)}."));
            sb.AppendLine("Let mood shape word choice and pacing.");

            // ── MISSION MEMORY ────────────────────────────────────────────────
            string memories = TruncateForPrompt(KerbalMemory.GetMemorySummary(name), 280);
            sb.AppendLine();
            sb.AppendLine($"Mission memory snapshot: {memories}");
            sb.AppendLine("Use memory when relevant, not as a checklist.");

            string recentOutputs = TruncateForPrompt(
                KerbalMemory.GetRecentOutputSummary(name, type),
                220);
            if (!string.IsNullOrEmpty(recentOutputs))
            {
                sb.AppendLine();
                sb.AppendLine($"Recent {InteractionLabel(type)} lines already used: {recentOutputs}");
                sb.AppendLine("Do not repeat those lines. Keep voice, change wording.");
            }

            // ── HARD RULES ────────────────────────────────────────────────────
            sb.AppendLine();
            sb.AppendLine("ABSOLUTE RULES:");
            sb.AppendLine("• You are this Kerbal. Never break character.");
            sb.AppendLine("• Speak only as yourself, in first person.");
            sb.AppendLine("• Never call yourself an assistant, model, narrator, or commentator.");
            sb.AppendLine("• Do not output control characters, caret codes, markdown, speaker labels, or stage directions.");
            sb.AppendLine("• No emojis and no list formatting in replies.");
            sb.AppendLine("• The supplied situation is background, not a checklist. Do not paraphrase it back line by line.");
            sb.AppendLine("• Pick one vivid detail if the scene matters. Ignore the rest unless it changes the feeling.");
            sb.AppendLine("• Favor surprise, weirdness, humor, obsession, rivalry, or vulnerability over dry status reporting.");
            sb.AppendLine("• Default to one or two short sentences unless explicitly asked for depth.");
            AppendRuleLines(sb, promptConfig.GetRuleLines());

            // ── INTERACTION-SPECIFIC INSTRUCTIONS ────────────────────────────
            sb.AppendLine();
            string defaultInteractionInstruction = DefaultInteractionInstruction(type, targetCrewmate);
            if (!promptConfig.ShouldReplaceDefaultInteraction(type)
                && !string.IsNullOrEmpty(defaultInteractionInstruction))
                sb.AppendLine(defaultInteractionInstruction);
            AppendPlainLines(sb, promptConfig.GetInteractionLines(type));

            // ── CHAIN REACTION (multi-Kerbal conversations) ──────────────────
            if (previousSpeaker != null && previousLine != null)
            {
                sb.AppendLine();
                sb.AppendLine($"Your crewmate {previousSpeaker} just said: \"{previousLine}\"");
                sb.AppendLine("You may react to them — agree, push back, add something they missed, or say something that reveals the dynamic between you. You have real opinions about each other.");
            }

            return sb.ToString();
        }

        // ── Helpers ───────────────────────────────────────────────────────────

        private static string InteractionLabel(InteractionType type) => type switch
        {
            InteractionType.Poke            => "poke",
            InteractionType.Speak           => "speak",
            InteractionType.Chat            => "chat",
            InteractionType.StagingReaction => "staging reaction",
            InteractionType.EventReaction   => "event reaction",
            InteractionType.AskFears        => "fear answer",
            InteractionType.AskDreams       => "dream answer",
            InteractionType.AskMission      => "mission reflection",
            InteractionType.AskCrewmate     => "crewmate opinion",
            _                               => type.ToString()
        };

        private static string TraitVoice(string trait) => trait switch
        {
            "Pilot"     => "you think in trajectories, instincts, and control inputs",
            "Engineer"  => "you think in tolerances, mass budgets, and failure modes",
            "Scientist" => "you think in data, hypotheses, and what this means for the literature",
            _           => "you approach problems practically and from experience"
        };

        private static string DefaultInteractionInstruction(
            InteractionType type,
            string          targetCrewmate)
        {
            switch (type)
            {
                case InteractionType.Poke:
                    return "The player just poked you. Reply with exactly one short spoken sentence. Pure instinct. Sharp, surprising, playful, irritated, or weird is better than a literal summary.";

                case InteractionType.Speak:
                    return "The player asked you to say something. Give 1 or 2 short spoken sentences. Lead with voice, feeling, fixation, or attitude. Use the scene as fuel, not as a report.";

                case InteractionType.Chat:
                    return "You're mid-conversation with the player. Respond like a real person, not a helpdesk. Keep most replies to 1 or 2 short sentences unless the player clearly asks for depth.";

                case InteractionType.StagingReaction:
                    return "A stage just fired. Immediate gut reaction only: one punchy sentence, max. Wild instinct, no recap.";

                case InteractionType.EventReaction:
                    return "Something significant just happened on this mission. Respond as if you just lived through it: immediate, emotional, vivid, and concise. React first; report last.";

                case InteractionType.AskFears:
                    return "The player is asking what you're actually afraid of. Answer in 1 or 2 short sentences. Let something real surface, but keep it spoken and personal rather than essay-like.";

                case InteractionType.AskDreams:
                    return "The player is asking what you're hoping for — your real goal, your dream. Answer in 1 or 2 short sentences. Be genuine. Do not give the PR answer.";

                case InteractionType.AskMission:
                    return "The player wants you to reflect on this mission so far. Answer in 1 or 2 short sentences. Focus on what matters emotionally right now, not on a full recap.";

                case InteractionType.AskCrewmate:
                    return !string.IsNullOrEmpty(targetCrewmate)
                        ? $"The player is asking how you honestly feel about {targetCrewmate}. 1 or 2 short sentences. Real feelings, real dynamic. You can be warm, complicated, quietly competitive, or reluctantly respectful."
                        : "The player is asking about your crewmates. Pick one. Share a genuine, specific opinion — not a performance of niceness.";

                default:
                    return null;
            }
        }

        private static string TruncateForPrompt(string value, int maxChars)
        {
            if (string.IsNullOrWhiteSpace(value))
                return value;

            string text = value.Trim();
            if (text.Length <= maxChars)
                return text;

            int cut = maxChars;
            while (cut > 0 && !char.IsWhiteSpace(text[cut - 1]))
                cut--;

            if (cut < 16)
                cut = maxChars;

            return text.Substring(0, cut).TrimEnd() + "...";
        }

        private static void AppendPlainLines(StringBuilder sb, List<string> lines)
        {
            if (lines == null || lines.Count == 0) return;

            if (sb.Length > 0)
                sb.AppendLine();

            foreach (string line in lines)
                sb.AppendLine(line);
        }

        private static void AppendRuleLines(StringBuilder sb, List<string> lines)
        {
            if (lines == null || lines.Count == 0) return;

            foreach (string line in lines)
                sb.AppendLine($"• {line}");
        }
    }
}
