using UnityEngine;
using UnityEngine.AI;

namespace ServiceSim
{
    /// <summary>混み具合の3段階</summary>
    public enum CrowdLevel
    {
        Empty = 0,    // 空いている
        Normal = 1,   // 普通
        Busy = 2      // 混雑
    }

    /// <summary>スタッフの仕事</summary>
    public enum StaffTask
    {
        Serve = 0,      // 料理を運ぶ（受け渡し口 → 卓 → 受け渡し口）
        Clear = 1,      // 下げ膳（卓 → 返却口）
        Pantry = 2,     // パントリーに立ち寄る
        Guide = 3,      // 待合の客を卓へ案内する
        Register = 4    // レジ
    }

    /// <summary>歩く客の行き先</summary>
    public enum GuestTrip
    {
        Toilet = 0,     // 卓 → トイレ → 卓
        DrinkBar = 1,   // 卓 → ドリンクバー → 卓
        Exit = 2        // 卓 → レジ → 入口から出る
    }

    /// <summary>
    /// 人の数字。人に関する数字はすべてこの .asset に置く。
    /// </summary>
    [CreateAssetMenu(menuName = "ServiceSim/People Spec", fileName = "PeopleSpec")]
    public class PeopleSpec : ScriptableObject
    {
        [Header("体の大きさ（箱と円柱で作る）")]
        [Tooltip("体の半径（m）")]
        public float personRadius = 0.2f;

        [Tooltip("身長（m）")]
        public float personHeight = 1.7f;

        [Tooltip("座っている客の形の高さ（m・床から頭の先まで）。見た目だけ。" +
                 "背もたれ（1.1 m）より少し上に出し、仕切り（1.2 m）よりも高くして、" +
                 "真上から見ても隠れないようにする。立っている客（1.7 m）より低くする")]
        public float seatedBodyHeight = 1.35f;

        [Tooltip("座っている客の形が腰かける高さ（m）。店の形の JSON の " +
                 "meta.heights.bench_seat / chair（0.45 m）に合わせる。見た目だけ")]
        public float seatedSeatHeight = 0.45f;

        [Header("スタッフ")]
        [Tooltip("スタッフの人数")]
        public int staffCount = 2;

        [Tooltip("スタッフの歩く速さ（m/秒）")]
        public float staffSpeed = 1.0f;

        [Tooltip("仕事を選ぶ重み。[料理を運ぶ, 下げ膳, パントリー, 案内, レジ]。初期値は同じ割合")]
        public float[] staffTaskWeights = { 1f, 1f, 1f, 1f, 1f };

        [Tooltip("受け渡し口で料理を取る時間（秒）")]
        public float staffDwellAtPassSec = 3f;

        [Tooltip("卓で料理を出す・下げる時間（秒）")]
        public float staffDwellAtTableSec = 5f;

        [Tooltip("返却口に皿を戻す時間（秒）")]
        public float staffDwellAtReturnSec = 5f;

        [Tooltip("パントリーに立ち寄る時間（秒）")]
        public float staffDwellAtPantrySec = 5f;

        [Tooltip("待合で客に声をかける時間（秒）")]
        public float staffDwellAtWaitingSec = 5f;

        [Tooltip("レジにいる時間（秒）")]
        public float staffDwellAtRegisterSec = 30f;

        [Header("歩く客")]
        [Tooltip("混み具合ごとの、歩く客の人数。[空いている, 普通, 混雑]")]
        public int[] guestCountByCrowd = { 1, 4, 8 };

        [Tooltip("歩く客の速さ（m/秒）")]
        public float guestSpeed = 0.8f;

        [Tooltip("行き先を選ぶ重み。[トイレ, ドリンクバー, 帰る]")]
        public float[] guestTripWeights = { 1f, 1f, 1f };

        [Tooltip("トイレ・ドリンクバーで過ごす時間（秒）")]
        public float guestDwellAtPoiSec = 10f;

        [Tooltip("レジで支払う時間（秒）")]
        public float guestDwellAtRegisterSec = 20f;

        [Tooltip("卓に戻って座っている時間（秒）")]
        public float guestDwellAtTableSec = 30f;

        [Header("慣らし（始めの埋まり方）")]
        [Tooltip("始めるときに、食事の途中の組で埋めておく卓の割合")]
        public float warmupOccupancy = 0.8f;

        [Header("人どうしのすれ違い")]
        [Tooltip("前に進めない状態がこの秒数続いたら、体を横に向ける（半径を細める）")]
        public float jamNarrowAfterSec = 1.0f;

        [Tooltip("体を横に向けたときの半径（m）")]
        public float jamNarrowRadius = 0.12f;

        [Tooltip("それでも抜けられないとき、下がるまでの秒数")]
        public float jamBackOffAfterSec = 3.0f;

        [Tooltip("これより遅ければ「前に進めない」とみなす（m/秒）")]
        public float jamStuckSpeed = 0.05f;

        [Header("待合に立つ客")]
        [Tooltip("混み具合ごとの、待合に立つ客の人数。[空いている, 普通, 混雑]")]
        public int[] waitingCountByCrowd = { 0, 2, 5 };

        [Tooltip("待合で立ち止まったまま、位置を少し変えるまでの時間（秒）")]
        public float waitingShuffleSec = 25f;

        [Header("椅子を引く（人の動きに合わせて引く）")]
        [Tooltip("引いた椅子をそのままにしておく時間（秒）。座る・立つときに引いて、この秒数で戻す")]
        public float chairPullHoldSec = 30f;

        [Tooltip("椅子を通路側へ引く距離（m）")]
        public float chairPullDistance = 0.3f;

        [Tooltip("食事中に何もなく引く分の、1卓あたりの平均の間隔（秒）。" +
                 "座る・立つときの引きとは別。0 以下なら引かない")]
        public float chairPullIdleMeanIntervalSec = 1800f;

        [Header("あきらめて、やり直す（B1）")]
        [Tooltip("客：行き先へ向かってこの秒数で着けなければ、あきらめて席に戻る")]
        public float guestGiveUpSec = 60f;

        [Tooltip("客：席に戻るのにもこの秒数で着けなければ、席の位置へ移して座らせる")]
        public float guestWarpHomeSec = 60f;

        [Tooltip("スタッフ：行き先へ向かってこの秒数で着けなければ、仕事を板に戻す")]
        public float staffGiveUpSec = 60f;

        [Tooltip("スタッフ：同じ仕事をこの回数あきらめたら、その場所へ移して仕事を済ませる")]
        public int staffGiveUpMaxTries = 3;

        [Header("よけ方（人の側がよける）")]
        [Tooltip("レジの内側（店員側）に使う NavMesh の区画の番号。" +
                 "客の NavMeshAgent はこの区画を外して歩くので、店員側に入れない。" +
                 "0=Walkable・1=Not Walkable・2=Jump は使えないので 3 以上にする")]
        public int registerBackArea = 3;

        [Tooltip("人の譲り合いの優先度。ロボットより大きくして、人がよけるようにする")]
        public int avoidancePriority = 50;

        [Tooltip("自動でよける機能の質。人はこれを使ってよい")]
        public ObstacleAvoidanceType avoidanceQuality = ObstacleAvoidanceType.GoodQualityObstacleAvoidance;

        [Tooltip("人の加速（m/秒^2）")]
        public float acceleration = 2.0f;

        [Tooltip("人の向きを変える速さ（度/秒）")]
        public float angularSpeedDegPerSec = 360f;

        [Tooltip("行き先にこれだけ近づいたら着いたとみなす（m）")]
        public float stoppingDistance = 0.3f;

        [Header("ロボットをよける（人の側がよける。値はすべて仮）")]
        [Tooltip("人がロボットをよけるか。切ると前のとおり気にせず歩く（比べるときに使う）")]
        public bool robotYieldEnabled = true;

        [Tooltip("細い通路に入る前に、経路の先をどこまで見るか（m）")]
        public float robotAisleLookAheadM = 3.0f;

        [Tooltip("通路の中にロボットがいるとみなす、通路のまわりの余白（m）")]
        public float robotAisleMarginM = 0.3f;

        [Tooltip("回り道してよい遠回りの上限（m）。これを超えるなら入口で待つ")]
        public float robotDetourExtraMaxM = 5.0f;

        [Tooltip("細い通路の中で向かい合ったとき、これを下回ったら下がってよける（m。外周どうしの空き）")]
        public float robotFaceToFaceM = 1.5f;

        [Tooltip("広い通路ですれ違うときに空ける距離（m。ロボットの外周から）")]
        public float robotKeepClearM = 0.3f;

        [Tooltip("ロボットの通り道からはずれるときの、横にずれる距離（m）")]
        public float robotSideStepM = 0.5f;

        [Tooltip("下がる先（よけ場所）を探す距離（m）")]
        public float robotBackOffSearchM = 4.0f;

        [Tooltip("ロボットが通り過ぎたとみなす、向かい合う距離からの余裕（m）")]
        public float robotPassedClearM = 0.5f;

        [Tooltip("待つ・下がる・回り道をあきらめて進むまでの秒数（秒）。B1 のあきらめより短くする")]
        public float robotYieldMaxSec = 20f;

        [Tooltip("上のよけ方を守らない割合（1回の行動につき）。スマホを見ている客などの代わり")]
        public float robotCarelessChance = 0.02f;

        [Tooltip("ロボットの外周からこの距離まで近づいたら contacts.csv に残す（m）")]
        public float robotContactGapM = 0.1f;

        [Tooltip("よけるときの横移動の速さ（m/秒）")]
        public float robotNudgeSpeedMps = 0.5f;

        // ---- 読み出し ----

        public int GuestCount(CrowdLevel crowd) { return Pick(guestCountByCrowd, crowd); }
        public int WaitingCount(CrowdLevel crowd) { return Pick(waitingCountByCrowd, crowd); }

        /// <summary>通路を歩く人の数（スタッフ＋歩く客）。待合に立つ客は数えない</summary>
        public int WalkerCount(CrowdLevel crowd) { return staffCount + GuestCount(crowd); }

        static int Pick(int[] values, CrowdLevel crowd)
        {
            if (values == null || values.Length == 0) return 0;
            int i = Mathf.Clamp((int)crowd, 0, values.Length - 1);
            return values[i];
        }

        /// <summary>重みで仕事を選ぶ</summary>
        public StaffTask PickStaffTask(System.Random rng)
        {
            int i = PickWeighted(staffTaskWeights, 5, rng);
            return (StaffTask)i;
        }

        /// <summary>重みで行き先を選ぶ</summary>
        public GuestTrip PickGuestTrip(System.Random rng)
        {
            int i = PickWeighted(guestTripWeights, 3, rng);
            return (GuestTrip)i;
        }

        static int PickWeighted(float[] weights, int count, System.Random rng)
        {
            float total = 0f;
            for (int i = 0; i < count; i++) total += WeightAt(weights, i);
            if (total <= 0f) return rng.Next(count);

            float r = (float)rng.NextDouble() * total;
            for (int i = 0; i < count; i++)
            {
                r -= WeightAt(weights, i);
                if (r <= 0f) return i;
            }
            return count - 1;
        }

        static float WeightAt(float[] weights, int i)
        {
            if (weights == null || i >= weights.Length) return 0f;
            return Mathf.Max(0f, weights[i]);
        }
    }
}
