using System.Collections.Generic;
using RudderSdk.Unity;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;

namespace LiveOpsExamples.Editor
{
  /// <summary>
  /// Bakes the Cozy Collector demo scene (world + camera + game setup) and saves it
  /// to <see cref="ScenePath"/>. UI is built by the CozyCollectorBake.Ui part.
  /// Re-running overwrites the same scene asset.
  /// </summary>
  public static partial class CozyCollectorBake
  {
    private const string ScenePath = "Assets/LiveOpsExamples/LiveOpsCozyCollector.unity";
    private const string BackgroundSpritePath = "Assets/LiveOpsExamples/Sprites/cozy-camp-bg.png";
    private const string PlayerSpritePath = "Assets/LiveOpsExamples/Sprites/player.png";
    private const string ConfigurationPath = "Assets/LiveOpsLocal.asset";
    private const string SpriteLibraryPath = "Assets/LiveOpsExamples/CozyCollectorSpriteLibrary.asset";
    private const string SnackPrefabPath = "Assets/LiveOpsExamples/Prefabs/Snack.prefab";
    private const string ScorePopupPrefabPath = "Assets/LiveOpsExamples/Prefabs/ScorePopup.prefab";
    private const string CollectBurstPrefabPath = "Assets/LiveOpsExamples/Prefabs/CollectBurst.prefab";

    // The Phaser field is 1280x720 px at 100 PPU -> 12.8x7.2 world units.
    private const float FieldWidthUnits = 12.8f;
    private const float FieldHeightUnits = 7.2f;

    private static readonly Color CameraBackgroundColor = new Color(0.10f, 0.08f, 0.13f);

    [MenuItem("Tools/LiveOps/Bake Cozy Collector (All)")]
    public static void BakeAll()
    {
      BakeAssets();
      BakePrefabs();
      BakeScene();
      AssetDatabase.SaveAssets();
    }

    [MenuItem("Tools/LiveOps/Bake Cozy Collector Scene")]
    public static void BakeScene()
    {
      var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);

      BuildSceneCamera();
      BuildSceneBackground();

      var world = BuildSceneWorld(out var playerRenderer, out var spawner, out var snacksParent);
      var controller = BuildSceneGameSetup(world);

      WireSceneWorld(world, playerRenderer, spawner, controller);
      WireSceneSpawner(spawner, snacksParent);

      BuildUi(controller, world);

      EditorSceneManager.SaveScene(scene, ScenePath);
      AddSceneToBuildSettings();
      AssetDatabase.SaveAssets();

      Debug.Log($"[CozyCollectorBake] Scene saved to {ScenePath}.");
    }

    private static void BuildSceneCamera()
    {
      var cameraGo = new GameObject("Main Camera");
      cameraGo.tag = "MainCamera";
      cameraGo.transform.position = new Vector3(0f, 0f, -10f);

      var camera = cameraGo.AddComponent<Camera>();
      camera.orthographic = true;
      camera.orthographicSize = FieldHeightUnits / 2f;
      camera.clearFlags = CameraClearFlags.SolidColor;
      camera.backgroundColor = CameraBackgroundColor;
      // Game field occupies the left 75% of the screen; the right side hosts the UI rail.
      camera.rect = new Rect(0f, 0f, 0.75f, 1f);
    }

    private static void BuildSceneBackground()
    {
      var backgroundGo = new GameObject("Background");
      var renderer = backgroundGo.AddComponent<SpriteRenderer>();
      renderer.sortingOrder = -10;

      var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(BackgroundSpritePath);
      if (sprite == null)
      {
        Debug.LogError($"[CozyCollectorBake] Background sprite not found at {BackgroundSpritePath}.");
        return;
      }

      renderer.sprite = sprite;
      var size = sprite.bounds.size;
      backgroundGo.transform.localScale = new Vector3(
        size.x > 0f ? FieldWidthUnits / size.x : 1f,
        size.y > 0f ? FieldHeightUnits / size.y : 1f,
        1f);
    }

    private static GameWorld BuildSceneWorld(
      out SpriteRenderer playerRenderer,
      out SnackSpawner spawner,
      out Transform snacksParent)
    {
      var worldGo = new GameObject("World");
      var world = worldGo.AddComponent<GameWorld>();

      var playerGo = new GameObject("Player");
      playerGo.transform.SetParent(worldGo.transform, false);
      playerRenderer = playerGo.AddComponent<SpriteRenderer>();
      playerRenderer.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(PlayerSpritePath);

      var colliderRadius = 0.3f;
      if (playerRenderer.sprite == null)
      {
        Debug.LogError($"[CozyCollectorBake] Player sprite not found at {PlayerSpritePath}.");
      }
      else
      {
        // Phaser setDisplaySize(82, 108) at 100 PPU -> 0.82 x 1.08 world units.
        var bounds = playerRenderer.sprite.bounds.size;
        var scale = new Vector3(0.82f / bounds.x, 1.08f / bounds.y, 1f);
        playerGo.transform.localScale = scale;
        // Phaser body is ~0.45x0.55 world units; a circle collider's world
        // radius scales by the largest transform axis.
        colliderRadius = 0.24f / Mathf.Max(scale.x, scale.y);
      }

      var body = playerGo.AddComponent<Rigidbody2D>();
      body.bodyType = RigidbodyType2D.Kinematic;
      body.simulated = true;
      body.gravityScale = 0f;

      // Non-trigger collider so the snacks' trigger colliders fire OnTriggerEnter2D.
      var collider = playerGo.AddComponent<CircleCollider2D>();
      collider.radius = colliderRadius;
      collider.isTrigger = false;

      var snacksGo = new GameObject("Snacks");
      snacksGo.transform.SetParent(worldGo.transform, false);
      snacksParent = snacksGo.transform;

      var spawnerGo = new GameObject("Spawner");
      spawnerGo.transform.SetParent(worldGo.transform, false);
      spawner = spawnerGo.AddComponent<SnackSpawner>();

      return world;
    }

    private static CozyCollectorController BuildSceneGameSetup(GameWorld world)
    {
      var setupGo = new GameObject("GameSetup");

      var rudder = setupGo.AddComponent<Rudder>();
      var configuration = AssetDatabase.LoadAssetAtPath<RudderConfiguration>(ConfigurationPath);
      if (configuration == null)
      {
        Debug.LogError($"[CozyCollectorBake] RudderConfiguration not found at {ConfigurationPath}.");
      }
      else
      {
        SetSceneReference(rudder, "_configuration", configuration);
      }

      var controller = setupGo.AddComponent<CozyCollectorController>();
      SetSceneReference(controller, "world", world);
      return controller;
    }

    private static void WireSceneWorld(
      GameWorld world,
      SpriteRenderer playerRenderer,
      SnackSpawner spawner,
      CozyCollectorController controller)
    {
      SetSceneReference(world, "player", playerRenderer);
      SetSceneReference(world, "spawner", spawner);
      SetSceneReference(world, "controller", controller);
      // moveMin/moveMax keep the component defaults (-6,-3.2)/(6,3.2).

      var scorePopupPrefab = LoadScenePrefabComponent<ScorePopupView>(ScorePopupPrefabPath);
      if (scorePopupPrefab != null)
      {
        SetSceneReference(world, "scorePopupPrefab", scorePopupPrefab);
      }

      var collectBurstPrefab = LoadScenePrefabComponent<ParticleSystem>(CollectBurstPrefabPath);
      if (collectBurstPrefab != null)
      {
        SetSceneReference(world, "collectBurstPrefab", collectBurstPrefab);
      }
    }

    private static void WireSceneSpawner(SnackSpawner spawner, Transform snacksParent)
    {
      var snackPrefab = LoadScenePrefabComponent<Snack>(SnackPrefabPath);
      if (snackPrefab != null)
      {
        SetSceneReference(spawner, "prefab", snackPrefab);
      }

      var library = AssetDatabase.LoadAssetAtPath<CozyCollectorSpriteLibrary>(SpriteLibraryPath);
      if (library == null)
      {
        Debug.LogError($"[CozyCollectorBake] Sprite library not found at {SpriteLibraryPath}. " +
                       "Run Tools/LiveOps/Bake Cozy Collector (All) first.");
      }
      else
      {
        SetSceneReference(spawner, "library", library);
      }

      SetSceneReference(spawner, "parent", snacksParent);
      // spawnMin/spawnMax keep the component defaults.
    }

    private static void AddSceneToBuildSettings()
    {
      var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
      foreach (var entry in scenes)
      {
        if (entry.path == ScenePath)
        {
          return;
        }
      }

      scenes.Add(new EditorBuildSettingsScene(ScenePath, true));
      EditorBuildSettings.scenes = scenes.ToArray();
    }

    private static T LoadScenePrefabComponent<T>(string path) where T : Component
    {
      var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
      var component = prefab != null ? prefab.GetComponent<T>() : null;
      if (component == null)
      {
        Debug.LogError($"[CozyCollectorBake] Prefab with {typeof(T).Name} not found at {path}. " +
                       "Run Tools/LiveOps/Bake Cozy Collector (All) first; the scene is saved with empty references.");
      }

      return component;
    }

    private static void SetSceneReference(Object target, string propertyName, Object value)
    {
      var serialized = new SerializedObject(target);
      var property = serialized.FindProperty(propertyName);
      if (property == null)
      {
        Debug.LogError($"[CozyCollectorBake] Property '{propertyName}' not found on {target.GetType().Name}.");
        return;
      }

      property.objectReferenceValue = value;
      serialized.ApplyModifiedPropertiesWithoutUndo();
    }
  }
}
