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

namespace JebAI
{
    /// <summary>
    /// Identifies which game event triggered a crew reaction.
    /// Used to select context detail level and write appropriate event descriptions.
    /// </summary>
    public enum GameEvent
    {
        Staging,
        OrbitAchieved,
        SubOrbital,
        AtmosphericEntry,
        Landing,
        Splashdown,
        Crash,
        EVA,
        Docking,
        LowFuel,
        SphereOfInfluenceChange,
    }

    /// <summary>
    /// Builds compact, token-efficient context strings from live KSP game data.
    ///
    /// IMPORTANT: Methods here are only called at the moment of user interaction
    /// (button click, poke, event fire). They are NEVER called on a timer or in Update().
    /// </summary>
    public static class ContextBuilder
    {
        // ── Flight context snapshot ────────────────────────────────────────────

        /// <summary>
        /// Builds a compact plain-text snapshot of the vessel's current state.
        /// Only called when the player sends a message or triggers an interaction.
        /// </summary>
        public static string BuildFlightContext(Vessel v)
        {
            if (v == null) return "No active vessel in flight.";

            var sb = new StringBuilder();

            sb.AppendLine($"Vessel: {v.vesselName}");
            sb.AppendLine($"Situation: {DescribeSituation(v)} around {v.mainBody.displayName}");
            sb.AppendLine($"Crew: {DescribeCrewState(v)}");
            sb.AppendLine($"Immediate feel: {DescribeImmediateFeel(v)}");
            sb.AppendLine($"Pressure: {DescribePressure(v)}");

            if (v.orbit != null && v.situation >= Vessel.Situations.ORBITING)
                sb.AppendLine($"Orbit snapshot: Apoapsis {FormatAlt(v.orbit.ApA)}, Periapsis {FormatAlt(v.orbit.PeA)}");
            else if (v.altitude >= 1000d)
                sb.AppendLine($"Altitude band: {DescribeAltitudeBand(v.altitude)}");

            sb.AppendLine($"Mission time: {FormatMET(v.missionTime)}");

            return sb.ToString();
        }

        // ── Event-specific context ─────────────────────────────────────────────

        /// <summary>
        /// Builds context for a specific game event, appending a human-readable event description.
        /// </summary>
        public static string BuildEventContext(GameEvent evt, Vessel v)
        {
            string flightCtx = BuildFlightContext(v);
            string evtDesc = DescribeEvent(evt, v);
            return $"{flightCtx}\nEvent: {evtDesc}";
        }

        private static string DescribeEvent(GameEvent evt, Vessel v) => evt switch
        {
            GameEvent.Staging            => $"Stage {v?.currentStage + 1} was just activated — engines ignited or parts separated.",
            GameEvent.OrbitAchieved      => $"Stable orbit achieved around {v?.mainBody.displayName}! Apoapsis {FormatAlt(v?.orbit?.ApA ?? 0)}, Periapsis {FormatAlt(v?.orbit?.PeA ?? 0)}.",
            GameEvent.SubOrbital         => $"Vessel is on a suborbital trajectory over {v?.mainBody.displayName}. Re-entry may be imminent.",
            GameEvent.AtmosphericEntry   => $"Entering atmosphere at {v?.speed:F0} m/s. Heating expected. Periapsis: {FormatAlt(v?.orbit?.PeA ?? 0)}.",
            GameEvent.Landing            => $"Landed safely on {v?.mainBody.displayName} after {FormatMET(v?.missionTime ?? 0)}.",
            GameEvent.Splashdown         => $"Splashdown in the ocean of {v?.mainBody.displayName}!",
            GameEvent.Crash              => $"Crash detected near {v?.mainBody.displayName}! Parts may have been lost.",
            GameEvent.EVA                => $"A Kerbal stepped outside on EVA at {FormatAlt(v?.altitude ?? 0)} above {v?.mainBody.displayName}.",
            GameEvent.Docking            => "Docking sequence detected — proximity approach in progress.",
            GameEvent.LowFuel            => "Liquid fuel critically low (under 10%)! Engine cutoff may be imminent.",
            GameEvent.SphereOfInfluenceChange => $"Entered the sphere of influence of {v?.mainBody.displayName}.",
            _                            => "Unknown event."
        };

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

        private static string DescribeSituation(Vessel v)
        {
            return v.situation switch
            {
                Vessel.Situations.PRELAUNCH  => "Pre-launch on launchpad",
                Vessel.Situations.LANDED     => $"Landed on {v.mainBody.displayName}",
                Vessel.Situations.SPLASHED   => $"Floating in ocean of {v.mainBody.displayName}",
                Vessel.Situations.FLYING     => $"Flying in atmosphere of {v.mainBody.displayName}",
                Vessel.Situations.SUB_ORBITAL => "Suborbital trajectory",
                Vessel.Situations.ORBITING   => $"Orbiting {v.mainBody.displayName}",
                Vessel.Situations.ESCAPING   => $"Escaping {v.mainBody.displayName}'s SOI",
                Vessel.Situations.DOCKED     => "Docked",
                _ => v.situation.ToString()
            };
        }

        private static string GetCrewList(Vessel v)
        {
            var names = new List<string>();
            foreach (var c in v.GetVesselCrew()) names.Add(c.name);
            return names.Count > 0 ? string.Join(", ", names) : "Unmanned";
        }

        private static string DescribeCrewState(Vessel v)
        {
            List<ProtoCrewMember> crew = v.GetVesselCrew();
            if (crew == null || crew.Count == 0) return "unmanned";
            if (crew.Count == 1) return $"{crew[0].name} is alone";
            return $"{crew.Count} crew aboard: {GetCrewList(v)}";
        }

        private static string DescribeImmediateFeel(Vessel v)
        {
            if (v.isEVA)
                return "outside in a suit with open air or vacuum all around";

            if (v.externalTemperature > 1200f)
                return "the hull feels hot and the whole moment feels dangerous";

            if (v.geeForce > 4.5f)
                return "everything feels heavy, compressed, and a little violent";

            return v.situation switch
            {
                Vessel.Situations.PRELAUNCH => "coiled up and waiting for ignition",
                Vessel.Situations.LANDED => "finally on something solid, with a rare moment to breathe",
                Vessel.Situations.SPLASHED => "bobbing on water after a rough ride",
                Vessel.Situations.FLYING => "the craft feels loud, fast, and alive",
                Vessel.Situations.SUB_ORBITAL => "on the edge between ascent and fall",
                Vessel.Situations.ORBITING => "weightless and suspended above the world",
                Vessel.Situations.ESCAPING => "slipping away from the familiar into deep space",
                Vessel.Situations.DOCKED => "quiet, cramped, and mechanically still",
                _ => "surrounded by the strange tension of active spaceflight"
            };
        }

        private static string DescribePressure(Vessel v)
        {
            GetFuelStats(v, out double lfPct, out _, out _);

            if (lfPct >= 0d && lfPct < 10d)
                return "fuel is low enough to be part of every thought";

            if (v.externalTemperature > 1200f)
                return "heat is the dominant threat right now";

            if (v.geeForce > 4.5f)
                return "g-load is the dominant physical pressure";

            if (System.Math.Abs(v.verticalSpeed) > 60f)
                return "the vertical motion feels aggressive";

            if (v.situation == Vessel.Situations.ORBITING)
                return "the danger is quiet for the moment, not gone";

            return "nothing is obviously failing, but the mission still feels tense";
        }

        private static string DescribeAltitudeBand(double m)
        {
            if (m < 3000d) return "very low";
            if (m < 15000d) return "low sky";
            if (m < 70000d) return "high sky";
            if (m < 250000d) return "near space";
            return "deep space";
        }

        private static void GetFuelStats(Vessel v,
            out double lfPct, out double oxPct, out double mpPct)
        {
            double lfA = 0, lfM = 0, oxA = 0, oxM = 0, mpA = 0, mpM = 0;
            foreach (Part p in v.parts)
                foreach (PartResource r in p.Resources)
                {
                    switch (r.resourceName)
                    {
                        case "LiquidFuel":    lfA += r.amount; lfM += r.maxAmount; break;
                        case "Oxidizer":      oxA += r.amount; oxM += r.maxAmount; break;
                        case "MonoPropellant":mpA += r.amount; mpM += r.maxAmount; break;
                    }
                }

            lfPct = lfM > 0 ? (lfA / lfM) * 100 : -1;
            oxPct = oxM > 0 ? (oxA / oxM) * 100 : -1;
            mpPct = mpM > 0 ? (mpA / mpM) * 100 : -1;
        }

        private static string FormatAlt(double m)
        {
            if (m >= 1_000_000) return $"{m / 1_000_000:F2} Mm";
            if (m >= 1_000)     return $"{m / 1_000:F1} km";
            return $"{m:F0} m";
        }

        private static string FormatMET(double t)
        {
            // KSP uses 6-hour Kerbin days
            int d = (int)(t / 21600);
            int h = (int)((t % 21600) / 3600);
            int m = (int)((t % 3600) / 60);
            int s = (int)(t % 60);
            return d > 0 ? $"T+{d}d {h:D2}h {m:D2}m {s:D2}s"
                         : $"T+{h:D2}h {m:D2}m {s:D2}s";
        }
    }
}
