using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.AI.Navigation;
using UnityEngine;
using UnityEngine.AI;

public class MapGenManager : MonoBehaviour
{
    /* ------------------ INSPECTOR FIELDS ------------------ */
    [Header("Room Prefabs")]
    [SerializeField] private List<GameObject> mapPrefab = new();

    [Header("Player")]
    [SerializeField] private GameObject Player;

    [Header("Corridor Prefabs")]
    [SerializeField] private GameObject CorridorStraight;
    [SerializeField] private GameObject CorridorStraightUnlit;
    [SerializeField] private GameObject DoorCorridor;

    [Header("Layout")]
    [SerializeField] private MapLayout layout;

    [Header("NavMesh Settings")]
    [SerializeField] private bool useGlobalNavMesh = true;
    private NavMeshSurface globalNavMeshSurface;

    [Header("Generation Settings")]
    [SerializeField] private int RoomDistance = 3;

    private readonly Dictionary<Vector2Int, GameObject> gridToRoom = new();
    public event Action<MapGenManager> OnGenerationComplete;
    public IReadOnlyDictionary<Vector2Int, GameObject> GridToRoom => gridToRoom;

    void Start() => StartCoroutine(GenerateWithDelay());

    private IEnumerator GenerateWithDelay()
    {
        yield return null; // Počkej jeden frame
        GenerateFromLayout();
    }

    private void GenerateFromLayout()
    {
        if (layout == null || string.IsNullOrWhiteSpace(layout.grid))
        {
            Debug.LogError("Layout asset není přiřazený nebo je prázdný!");
            return;
        }
        gridToRoom.Clear();

        /* ----------- Create a spawn room ----------- */
        GameObject spawnPrefab = mapPrefab[0];
        Vector3 cellSize = GetPrefabXZ(spawnPrefab);

        string[] lines = layout.grid.Split('\n')
                                    .Select(l => l.TrimEnd('\r'))
                                    .Where(l => !string.IsNullOrWhiteSpace(l))
                                    .ToArray();
        Vector2 first = GetFirstOrLastRoom(lines);
        int bottomRow = (int)first.y;
        int spawnX = (int)first.x;

        Vector3 spawnPos = new Vector3(spawnX * (cellSize.x + RoomDistance), 0, 0);
        GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform);
        gridToRoom[new Vector2Int(spawnX, 0)] = spawnRoom;


        /* ----------- Spawn the player ----------- */
        if (Player)
        {
            GameObject p = Instantiate(Player, spawnPos + new Vector3(0, 1, 3), Quaternion.identity, transform);
            Transform inner = p.transform.Find("Player");
            if (inner) inner.tag = "Player";
        }

        /* ----------- Build rooms ----------- */
        BuildRooms(lines, bottomRow, cellSize);

        // Použij coroutine pro dokončení generování
        StartCoroutine(FinishGenerationAfterDelay());
    }

    /// <summary>
    /// Builds corridors and doors between rooms.
    /// </summary>
    private void BuildCorridors()
    {
        // Jediné dva směry, které musíme zkontrolovat z každé místnosti (right/up)
        Vector2Int[] directions = { Vector2Int.right, Vector2Int.up };

        // Délky prefebů – podél X a Z osy
        float straightLenX = CorridorStraight.GetComponent<PrefabSize>().prefabSize.x;
        float straightLenZ = CorridorStraight.GetComponent<PrefabSize>().prefabSize.y;
        float doorLenX = DoorCorridor.GetComponent<PrefabSize>().prefabSize.x;
        float doorLenZ = DoorCorridor.GetComponent<PrefabSize>().prefabSize.y;

        foreach (var kv in gridToRoom)
        {
            Vector2Int cell = kv.Key;
            GameObject roomA = kv.Value;

            foreach (Vector2Int dir in directions)
            {
                Vector2Int nKey = cell + dir;
                if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue;

                // Handle geometry
                Vector3 axis; float lenStraight, lenDoor; Quaternion rot;
                if (dir == Vector2Int.right)
                {
                    axis = Vector3.right;
                    lenStraight = straightLenX;
                    lenDoor = doorLenX;
                    rot = Quaternion.Euler(0, 90, 0);
                }
                else // Vector2Int.up
                {
                    axis = Vector3.forward;
                    lenStraight = straightLenZ;
                    lenDoor = doorLenZ;
                    rot = Quaternion.identity;
                }

                // Wall calculation
                float halfA = Vector3.Scale(GetPrefabXZ(roomA), axis).magnitude * 0.5f;
                float halfB = Vector3.Scale(GetPrefabXZ(roomB), axis).magnitude * 0.5f;
                Vector3 wallA = roomA.transform.position + axis * halfA;
                Vector3 wallB = roomB.transform.position - axis * halfB;

                // Doors
                Vector3 doorPos = (wallA + wallB) * 0.5f;
                GameObject doorGO = Instantiate(DoorCorridor, doorPos, rot, transform);
                DoorAnimation anim = doorGO.GetComponent<DoorAnimation>();

                // Register the corridor to both rooms
                RoomHandler rhA = roomA.GetComponent<RoomHandler>();
                RoomHandler rhB = roomB.GetComponent<RoomHandler>();
                if (dir == Vector2Int.right)
                {
                    rhA.RegisterDoor(RoomHandler.Side.East, anim);
                    rhB.RegisterDoor(RoomHandler.Side.West, anim);
                }
                else
                {
                    rhA.RegisterDoor(RoomHandler.Side.North, anim);
                    rhB.RegisterDoor(RoomHandler.Side.South, anim);
                }

                // ROVNÉ SEGMENTY z obou stran dveří
                Vector3 doorEdgeA = doorPos - axis * (lenDoor * 0.5f);
                Vector3 doorEdgeB = doorPos + axis * (lenDoor * 0.5f);

                PlaceStraightSegments(doorEdgeA, wallA, -axis, rot, lenStraight);
                PlaceStraightSegments(doorEdgeB, wallB, axis, rot, lenStraight);
            }
        }
    }

    /// <summary>
    /// Vyplní úsek mezi startEdge (hrana dveří nebo předchozího dílu)
    /// a wallEdge (vnější hrana stěny místnosti) rovnými segmenty tak,
    /// aby žádný segment nepřečníval do stěny.
    /// </summary>
    private void PlaceStraightSegments(
            Vector3 startEdge,
            Vector3 wallEdge,
            Vector3 direction,     
            Quaternion rot,
            float len)        
    {
        const float EPS = 0.01f;                      
        float dist = Vector3.Distance(startEdge, wallEdge);
        if (dist < EPS) return;                           

        int fullCount = Mathf.FloorToInt(dist / len);
        float remainder = dist - fullCount * len;     

        // Full segments
        Vector3 firstPivot = startEdge + direction * (len * 0.5f);
        for (int i = 0; i < fullCount; i++)
        {
            Vector3 pos = firstPivot + direction * (i * len);
            GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit;
            Instantiate(prefab, pos, rot, transform);
        }

        // Short segment to fill the gap
        if (remainder > EPS)
        {
            Vector3 remPivot = wallEdge - direction * (remainder * 0.5f);
            GameObject last = Instantiate(CorridorStraightUnlit, remPivot, rot, transform);

            Vector3 sc = last.transform.localScale;
            sc.z *= remainder / len;
            last.transform.localScale = sc;
        }
    }

    /* ============================================================ */
    /*  ROOMS                                                       */
    /* ============================================================ */
    private void BuildRooms(string[] lines, int bottomRowIdx, Vector3 cellSize)
    {
        for (int r = 0; r < lines.Length; r++)
        {
            string line = lines[r];
            int gridZ = bottomRowIdx - r + 1;
            for (int x = 0; x < line.Length; x++)
            {
                char ch = line[x];
                if (ch == '-' || !char.IsDigit(ch)) continue;
                int idx = ch - '0';
                if (idx >= mapPrefab.Count) continue;
                Vector3 pos = new Vector3(x * (cellSize.x + RoomDistance), 0, gridZ * (cellSize.z + RoomDistance));
                GameObject room = Instantiate(mapPrefab[idx], pos, Quaternion.identity, transform);
                gridToRoom[new Vector2Int(x, gridZ)] = room;
            }
        }
    }

    private IEnumerator FinishGenerationAfterDelay()
    {
        yield return new WaitForSeconds(0.5f);

        /* ----------- Set entrances ----------- */
        foreach (var kv in gridToRoom)
        {
            Vector2Int g = kv.Key;
            RoomHandler rh = kv.Value.GetComponent<RoomHandler>();
            bool north = gridToRoom.ContainsKey(g + Vector2Int.left);
            bool south = gridToRoom.ContainsKey(g + Vector2Int.right);
            bool east = gridToRoom.ContainsKey(g + Vector2Int.up);
            bool west = gridToRoom.ContainsKey(g + Vector2Int.down);
            rh.SetEntrances(north, south, east, west);
        }

        /* ----------- Build corridors ----------- */
        BuildCorridors();

        /* ----------- Toggle all doors ----------- */
        foreach (var keyValuePair in gridToRoom)
        {
            RoomHandler rh = keyValuePair.Value.GetComponent<RoomHandler>();
            rh.ToggleAllDoors();
        }

        // Vytvoř globální NavMesh
        if (useGlobalNavMesh)
        {
            yield return new WaitForSeconds(0.2f);
            CreateGlobalNavMesh();
        }

        OnGenerationComplete?.Invoke(this);
    }

    private void CreateGlobalNavMesh()
    {
        // Odstraň všechny existující NavMeshSurface z místností
        foreach (var room in gridToRoom.Values)
        {
            NavMeshSurface roomSurface = room.GetComponent<NavMeshSurface>();
            if (roomSurface != null)
            {
                Destroy(roomSurface);
            }
        }

        // Vytvoř globální NavMeshSurface
        globalNavMeshSurface = gameObject.AddComponent<NavMeshSurface>();
        globalNavMeshSurface.collectObjects = CollectObjects.Children;
        globalNavMeshSurface.useGeometry = NavMeshCollectGeometry.PhysicsColliders;

        // Bake globální NavMesh
        globalNavMeshSurface.BuildNavMesh();
    }

    /* ============================================================ */
    /*  HELPERS                                                     */
    /* ============================================================ */
    private static Vector3 GetPrefabXZ(GameObject prefab)
    {
        var ps = prefab.GetComponent<PrefabSize>();
        return ps ? new Vector3(ps.prefabSize.x, 0, ps.prefabSize.y) : new Vector3(10, 0, 10);
    }

    private Vector2 GetFirstOrLastRoom(string[] lines)
    {
        for (int r = lines.Length - 1; r >= 0; r--)
        {
            string l = lines[r];
            int c = l.ToCharArray().ToList().FindIndex(char.IsDigit);
            if (c != -1) return new Vector2(c, r);
        }
        Debug.LogError("Layout neobsahuje žádnou číslici (místnost)!");
        return Vector2.zero;
    }
}
