using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Tools.Build;

namespace MCPForUnity.Editor.Tools
{
    [McpForUnityTool("manage_build", AutoRegister = false, Group = "core",
        RequiresPolling = true, PollAction = "status", MaxPollSeconds = 1800)]
    public static class ManageBuild
    {
        private static readonly string[] ValidActions =
            { "build", "status", "platform", "settings", "scenes", "profiles", "batch", "cancel" };

        public static object HandleCommand(JObject @params)
        {
            if (@params == null)
                return new ErrorResponse("Parameters cannot be null.");

            var p = new ToolParams(@params);
            var actionResult = p.GetRequired("action");
            if (!actionResult.IsSuccess)
                return new ErrorResponse(actionResult.ErrorMessage);

            string action = actionResult.Value.ToLowerInvariant();

            if (!ValidActions.Contains(action))
                return new ErrorResponse(
                    $"Unknown action '{action}'. Valid actions: {string.Join(", ", ValidActions)}");

            try
            {
                switch (action)
                {
                    case "build": return HandleBuild(p);
                    case "status": return HandleStatus(p);
                    case "platform": return HandlePlatform(p);
                    case "settings": return HandleSettings(p);
                    case "scenes": return HandleScenes(p);
                    case "profiles": return HandleProfiles(p);
                    case "batch": return HandleBatch(p);
                    case "cancel": return HandleCancel(p);
                    default:
                        return new ErrorResponse($"Unknown action: '{action}'");
                }
            }
            catch (Exception ex)
            {
                return new ErrorResponse(ex.Message, new { stackTrace = ex.StackTrace });
            }
        }

        // ── build ──────────────────────────────────────────────────────

        private static object HandleBuild(ToolParams p)
        {
            if (BuildPipeline.isBuildingPlayer)
                return new ErrorResponse("A build is already in progress.");

            string targetName = p.Get("target");
            if (!BuildTargetMapping.TryResolveBuildTarget(targetName, out var target))
                return new ErrorResponse(BuildTargetMapping.GetUnknownBuildTargetMessage(targetName));

            var group = BuildTargetMapping.GetTargetGroup(target);
            if (!BuildPipeline.IsBuildTargetSupported(group, target))
                return new ErrorResponse(
                    $"Platform '{target}' is not installed. Install it via Unity Hub.");

            string outputPath = p.Get("output_path")
                ?? BuildTargetMapping.GetDefaultOutputPath(target, PlayerSettings.productName);
            string[] scenes = p.GetStringArray("scenes");
            bool development = p.GetBool("development");
            string[] optionNames = p.GetStringArray("options");
            string subtargetStr = p.Get("subtarget");
            string scriptingBackend = p.Get("scripting_backend");

            // Apply scripting backend if specified (persistent change)
            if (!string.IsNullOrEmpty(scriptingBackend))
            {
                string backendLower = scriptingBackend.ToLowerInvariant();
                if (backendLower != "il2cpp" && backendLower != "mono")
                    return new ErrorResponse(
                        $"Unknown scripting_backend '{scriptingBackend}'. Valid: mono, il2cpp");
                var namedTarget = BuildTargetMapping.GetNamedBuildTarget(target);
                var impl = backendLower == "il2cpp"
                    ? ScriptingImplementation.IL2CPP
                    : ScriptingImplementation.Mono2x;
                PlayerSettings.SetScriptingBackend(namedTarget, impl);
            }

#if UNITY_6000_0_OR_NEWER
            string profilePath = p.Get("profile");
            if (!string.IsNullOrEmpty(profilePath))
                return HandleProfileBuild(p, profilePath, outputPath, development, optionNames);
#else
            string profilePath = p.Get("profile");
            if (!string.IsNullOrEmpty(profilePath))
                McpLog.Warn($"Build Profile param ignored — requires Unity 6+. Current: {UnityEngine.Application.unityVersion}");
#endif

            var buildOptions = BuildRunner.ParseBuildOptions(optionNames, development);
            int subtarget = BuildTargetMapping.ResolveSubtarget(subtargetStr);
            var options = BuildRunner.CreateBuildOptions(target, outputPath, scenes, buildOptions, subtarget);

            string jobId = BuildJobStore.CreateJobId();
            var job = new BuildJob(jobId, target, outputPath);
            return BuildRunner.ScheduleBuild(job, options);
        }

#if UNITY_6000_0_OR_NEWER
        private static object HandleProfileBuild(ToolParams p, string profilePath, string outputPath,
            bool development, string[] optionNames)
        {
            var profile = UnityEditor.AssetDatabase.LoadAssetAtPath<
                UnityEditor.Build.Profile.BuildProfile>(profilePath);
            if (profile == null)
                return new ErrorResponse($"Build profile not found at: {profilePath}");

            var buildOptions = BuildRunner.ParseBuildOptions(optionNames, development);
            var options = new BuildPlayerWithProfileOptions
            {
                buildProfile = profile,
                locationPathName = outputPath,
                options = buildOptions
            };

            // BuildPlayerWithProfileOptions derives the actual target from the profile,
            // but we use activeBuildTarget for job metadata/status display
            var target = EditorUserBuildSettings.activeBuildTarget;
            string jobId = BuildJobStore.CreateJobId();
            var job = new BuildJob(jobId, target, outputPath);
            return BuildRunner.ScheduleProfileBuild(job, options);
        }
#endif

        // ── status ─────────────────────────────────────────────────────

        private static object HandleStatus(ToolParams p)
        {
            string jobId = p.Get("job_id");

            if (string.IsNullOrEmpty(jobId))
            {
                // Prefer active (pending/building) job — needed for polling middleware
                var last = BuildJobStore.LastCompletedJob;
                if (last != null && (last.State == BuildJobState.Building || last.State == BuildJobState.Pending))
                {
                    return new PendingResponse(
                        $"Build {last.State.ToString().ToLowerInvariant()}...",
                        pollIntervalSeconds: 5.0,
                        data: last.ToStatusResponse()
                    );
                }

                if (last != null)
                    return new SuccessResponse("Last completed build.", last.ToStatusResponse());

#if UNITY_6000_0_OR_NEWER
                var latestReport = BuildReport.GetLatestReport();
                if (latestReport != null)
                {
                    var s = latestReport.summary;
                    return new SuccessResponse("Last build report from Unity.", new
                    {
                        result = s.result.ToString().ToLowerInvariant(),
                        platform = s.platform.ToString(),
                        output_path = s.outputPath,
                        total_size_mb = Math.Round(s.totalSize / (1024.0 * 1024.0), 2),
                        duration_seconds = s.totalTime.TotalSeconds,
                        errors = s.totalErrors,
                        warnings = s.totalWarnings
                    });
                }
#endif
                return new ErrorResponse("No build jobs found.");
            }

            // Check batch jobs first
            var batchJob = BuildJobStore.GetBatchJob(jobId);
            if (batchJob != null)
                return new SuccessResponse($"Batch {batchJob.State}.", batchJob.ToStatusResponse());

            var buildJob = BuildJobStore.GetBuildJob(jobId);
            if (buildJob == null)
                return new ErrorResponse($"No job found with ID: {jobId}");

            if (buildJob.State == BuildJobState.Building || buildJob.State == BuildJobState.Pending)
            {
                return new PendingResponse(
                    $"Build {buildJob.State.ToString().ToLowerInvariant()}...",
                    pollIntervalSeconds: 5.0,
                    data: buildJob.ToStatusResponse()
                );
            }

            return new SuccessResponse($"Build {buildJob.State}.", buildJob.ToStatusResponse());
        }

        // ── platform ───────────────────────────────────────────────────

        private static object HandlePlatform(ToolParams p)
        {
            string targetName = p.Get("target");

            if (string.IsNullOrEmpty(targetName))
            {
                // Read current platform
                return new SuccessResponse("Current platform.", new
                {
                    target = EditorUserBuildSettings.activeBuildTarget.ToString(),
                    target_group = BuildTargetMapping.GetTargetGroup(
                        EditorUserBuildSettings.activeBuildTarget).ToString(),
                    subtarget = EditorUserBuildSettings.standaloneBuildSubtarget.ToString()
                });
            }

            // Switch platform
            if (!BuildTargetMapping.TryResolveBuildTarget(targetName, out var target))
                return new ErrorResponse(BuildTargetMapping.GetUnknownBuildTargetMessage(targetName));

            var group = BuildTargetMapping.GetTargetGroup(target);
            if (!BuildPipeline.IsBuildTargetSupported(group, target))
                return new ErrorResponse(
                    $"Platform '{target}' is not installed. Install it via Unity Hub.");

            if (EditorUserBuildSettings.activeBuildTarget == target)
                return new SuccessResponse("Already on this platform.", new
                {
                    target = target.ToString()
                });

            // Capture previous target before switching
            string previousTarget = EditorUserBuildSettings.activeBuildTarget.ToString();

            string subtargetStr = p.Get("subtarget");
            if (!string.IsNullOrEmpty(subtargetStr))
            {
                string subtargetLower = subtargetStr.ToLowerInvariant();
                if (subtargetLower == "server")
                    EditorUserBuildSettings.standaloneBuildSubtarget = StandaloneBuildSubtarget.Server;
                else if (subtargetLower == "player")
                    EditorUserBuildSettings.standaloneBuildSubtarget = StandaloneBuildSubtarget.Player;
            }

            // SwitchActiveBuildTarget is synchronous — blocks until reimport completes
            EditorUserBuildSettings.SwitchActiveBuildTarget(group, target);

            return new SuccessResponse(
                $"Switched to {target}. Assets reimported for new platform.",
                new { target = target.ToString(), previous = previousTarget }
            );
        }

        // ── settings ───────────────────────────────────────────────────

        private static object HandleSettings(ToolParams p)
        {
            var propertyResult = p.GetRequired("property");
            if (!propertyResult.IsSuccess)
                return new ErrorResponse(propertyResult.ErrorMessage);

            string property = propertyResult.Value.ToLowerInvariant();
            string targetName = p.Get("target");
            string value = p.Get("value");

            // Resolve target
            string err = BuildTargetMapping.TryResolveNamedBuildTarget(targetName, out var namedTarget);
            if (err != null)
                return new ErrorResponse(err);

            if (string.IsNullOrEmpty(value))
            {
                // Read
                var result = BuildSettingsHelper.ReadProperty(property, namedTarget);
                if (result == null)
                    return new ErrorResponse(
                        $"Unknown property '{property}'. Valid: {string.Join(", ", BuildSettingsHelper.ValidProperties)}");
                return new SuccessResponse($"Read {property}.", result);
            }

            // Write
            string writeErr = BuildSettingsHelper.WriteProperty(property, value, namedTarget);
            if (writeErr != null)
                return new ErrorResponse(writeErr);
            return new SuccessResponse($"Set {property} = {value}.",
                BuildSettingsHelper.ReadProperty(property, namedTarget));
        }

        // ── scenes ─────────────────────────────────────────────────────

        private static object HandleScenes(ToolParams p)
        {
            var scenesRaw = p.GetRaw("scenes");

            if (scenesRaw == null || scenesRaw.Type == JTokenType.Null)
            {
                // Read current scene list
                var scenes = EditorBuildSettings.scenes.Select(s => new
                {
                    path = s.path,
                    enabled = s.enabled,
                    guid = s.guid.ToString()
                }).ToArray();

                return new SuccessResponse($"Build scenes ({scenes.Length}).", new { scenes });
            }

            // Handle string input: try JSON parse, then comma-separated
            if (scenesRaw.Type == JTokenType.String)
            {
                string scenesStr = scenesRaw.ToString();
                try { scenesRaw = JArray.Parse(scenesStr); }
                catch
                {
                    // Treat as comma-separated paths
                    var paths = scenesStr.Split(',')
                        .Select(s => s.Trim())
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
                    if (paths.Length == 0)
                        return new ErrorResponse("'scenes' string contained no valid paths.");
                    var fromStr = paths.Select(sp => new EditorBuildSettingsScene(sp, true)).ToArray();
                    EditorBuildSettings.scenes = fromStr;
                    return new SuccessResponse($"Updated build scenes ({fromStr.Length}).", new
                    {
                        scenes = fromStr.Select(s => new { path = s.path, enabled = s.enabled }).ToArray()
                    });
                }
            }

            // Write scene list — accepts array of strings or array of {path, enabled} objects
            var sceneArray = scenesRaw as JArray;
            if (sceneArray == null)
                return new ErrorResponse("'scenes' must be an array of scene paths or {path, enabled} objects.");

            var newScenes = new List<EditorBuildSettingsScene>();
            foreach (var item in sceneArray)
            {
                if (item.Type == JTokenType.String)
                {
                    newScenes.Add(new EditorBuildSettingsScene(item.ToString(), true));
                    continue;
                }
                string path = item["path"]?.ToString();
                if (string.IsNullOrEmpty(path))
                    return new ErrorResponse("Each scene must have a 'path' field.");
                bool enabled = item["enabled"]?.Value<bool>() ?? true;
                newScenes.Add(new EditorBuildSettingsScene(path, enabled));
            }

            EditorBuildSettings.scenes = newScenes.ToArray();
            return new SuccessResponse($"Updated build scenes ({newScenes.Count}).", new
            {
                scenes = newScenes.Select(s => new { path = s.path, enabled = s.enabled }).ToArray()
            });
        }

        // ── profiles ───────────────────────────────────────────────────

        private static object HandleProfiles(ToolParams p)
        {
#if UNITY_6000_0_OR_NEWER
            string profilePath = p.Get("profile");
            bool activate = p.GetBool("activate");

            if (string.IsNullOrEmpty(profilePath))
            {
                // List all profiles
                var guids = AssetDatabase.FindAssets("t:BuildProfile");
                var profiles = guids.Select(guid =>
                {
                    string path = AssetDatabase.GUIDToAssetPath(guid);
                    return new { path, name = System.IO.Path.GetFileNameWithoutExtension(path) };
                }).ToArray();

                var active = UnityEditor.Build.Profile.BuildProfile.GetActiveBuildProfile();
                return new SuccessResponse($"Found {profiles.Length} build profiles.", new
                {
                    profiles,
                    active_profile = active != null ? AssetDatabase.GetAssetPath(active) : null
                });
            }

            var loadedProfile = AssetDatabase.LoadAssetAtPath<
                UnityEditor.Build.Profile.BuildProfile>(profilePath);
            if (loadedProfile == null)
                return new ErrorResponse($"Build profile not found at: {profilePath}");

            if (activate)
            {
                UnityEditor.Build.Profile.BuildProfile.SetActiveBuildProfile(loadedProfile);
                return new SuccessResponse($"Activated build profile: {profilePath}", new
                {
                    profile = profilePath,
                    activated = true
                });
            }

            // Get profile details — use .scenes (available since Unity 6000.0.0)
            // instead of .GetScenesForBuild() which was added in 6000.0.36
            var profileScenes = loadedProfile.scenes
                .Select(s => s.path).ToArray();
            return new SuccessResponse($"Profile: {profilePath}", new
            {
                profile = profilePath,
                scenes = profileScenes
            });
#else
            string version = UnityEngine.Application.unityVersion;
            return new ErrorResponse(
                $"Build Profiles require Unity 6 (6000.0+). Current version: {version}");
#endif
        }

        // ── batch ──────────────────────────────────────────────────────

        private static object HandleBatch(ToolParams p)
        {
            if (BuildPipeline.isBuildingPlayer)
                return new ErrorResponse("A build is already in progress.");

            string[] targets = p.GetStringArray("targets");
            string[] profiles = p.GetStringArray("profiles");
            string outputDir = p.Get("output_dir") ?? "Builds";
            bool development = p.GetBool("development");
            string[] optionNames = p.GetStringArray("options");

            if ((targets == null || targets.Length == 0) && (profiles == null || profiles.Length == 0))
                return new ErrorResponse("'targets' or 'profiles' is required for batch builds.");
            if (targets != null && targets.Length > 0 && profiles != null && profiles.Length > 0)
                return new ErrorResponse("Provide 'targets' or 'profiles', not both.");

            // Validate all targets/profiles upfront before creating store entries
            if (targets != null && targets.Length > 0)
            {
                var resolvedTargets = new List<(BuildTarget bt, string path)>();
                foreach (var t in targets)
                {
                    if (!BuildTargetMapping.TryResolveBuildTarget(t, out var bt))
                        return new ErrorResponse(BuildTargetMapping.GetUnknownBuildTargetMessage(t));
                    var btGroup = BuildTargetMapping.GetTargetGroup(bt);
                    if (!BuildPipeline.IsBuildTargetSupported(btGroup, bt))
                        return new ErrorResponse(
                            $"Platform '{bt}' is not installed. Install it via Unity Hub.");
                    string defaultPath = BuildTargetMapping.GetDefaultOutputPath(bt, PlayerSettings.productName);
                    string path = defaultPath.StartsWith("Builds/")
                        ? $"{outputDir}/{defaultPath.Substring(7)}"
                        : $"{outputDir}/{defaultPath}";
                    resolvedTargets.Add((bt, path));
                }

                string batchId = BuildJobStore.CreateBatchId();
                var batch = new BatchJob(batchId);
                batch.State = BuildJobState.Building;
                BuildJobStore.AddBatchJob(batch);

                foreach (var (bt, path) in resolvedTargets)
                {
                    var child = new BuildJob(BuildJobStore.CreateJobId(), bt, path);
                    batch.Children.Add(child);
                }

                var buildOpts = BuildRunner.ParseBuildOptions(optionNames, development);

                BuildRunner.ScheduleNextBatchBuild(batch, index =>
                {
                    var child = batch.Children[index];
                    var group = BuildTargetMapping.GetTargetGroup(child.Target);

                    // Platform switch is required — ensures correct shader variants,
                    // asset import settings, and scripting defines for the target
                    if (EditorUserBuildSettings.activeBuildTarget != child.Target)
                        EditorUserBuildSettings.SwitchActiveBuildTarget(group, child.Target);

                    int subtarget = (int)StandaloneBuildSubtarget.Player;
                    var options = BuildRunner.CreateBuildOptions(
                        child.Target, child.OutputPath, null, buildOpts, subtarget);
                    BuildRunner.ScheduleBuild(child, options);
                    return child;
                });

                return new PendingResponse(
                    $"Batch build started ({batch.Children.Count} builds).",
                    pollIntervalSeconds: 10.0,
                    data: new { job_id = batchId, total = batch.Children.Count }
                );
            }
#if UNITY_6000_0_OR_NEWER
            if (profiles != null && profiles.Length > 0)
            {
                // Validate all profiles exist before creating any store entries
                var loadedProfiles = new List<UnityEditor.Build.Profile.BuildProfile>();
                foreach (var profilePath in profiles)
                {
                    var profile = AssetDatabase.LoadAssetAtPath<
                        UnityEditor.Build.Profile.BuildProfile>(profilePath);
                    if (profile == null)
                        return new ErrorResponse($"Profile not found: {profilePath}");
                    loadedProfiles.Add(profile);
                }

                string batchId = BuildJobStore.CreateBatchId();
                var batch = new BatchJob(batchId);
                batch.State = BuildJobState.Building;
                BuildJobStore.AddBatchJob(batch);

                for (int i = 0; i < profiles.Length; i++)
                {
                    var target = EditorUserBuildSettings.activeBuildTarget;
                    string name = System.IO.Path.GetFileNameWithoutExtension(profiles[i]);
                    string path = $"{outputDir}/{name}/{PlayerSettings.productName}";
                    var child = new BuildJob(BuildJobStore.CreateJobId(), target, path);
                    batch.Children.Add(child);
                }

                var buildOpts = BuildRunner.ParseBuildOptions(optionNames, development);

                BuildRunner.ScheduleNextBatchBuild(batch, index =>
                {
                    var child = batch.Children[index];
                    var opts = new BuildPlayerWithProfileOptions
                    {
                        buildProfile = loadedProfiles[index],
                        locationPathName = child.OutputPath,
                        options = buildOpts
                    };
                    BuildRunner.ScheduleProfileBuild(child, opts);
                    return child;
                });

                return new PendingResponse(
                    $"Batch build started ({batch.Children.Count} builds).",
                    pollIntervalSeconds: 10.0,
                    data: new { job_id = batchId, total = batch.Children.Count }
                );
            }
#else
            if (profiles != null && profiles.Length > 0)
            {
                return new ErrorResponse(
                    $"Profile-based batch requires Unity 6+. Current: {UnityEngine.Application.unityVersion}");
            }
#endif

            return new ErrorResponse("'targets' or 'profiles' is required for batch builds.");
        }

        // ── cancel ─────────────────────────────────────────────────────

        private static object HandleCancel(ToolParams p)
        {
            var jobIdResult = p.GetRequired("job_id");
            if (!jobIdResult.IsSuccess)
                return new ErrorResponse(jobIdResult.ErrorMessage);

            string jobId = jobIdResult.Value;

            var batchJob = BuildJobStore.GetBatchJob(jobId);
            if (batchJob != null)
            {
                if (batchJob.State == BuildJobState.Building)
                {
                    batchJob.State = BuildJobState.Cancelled;
                    return new SuccessResponse(
                        "Batch cancelled. The current build will finish but no more builds will start.",
                        new { job_id = jobId, state = "cancelled" });
                }
                return new ErrorResponse($"Batch is already {batchJob.State}.");
            }

            var buildJob = BuildJobStore.GetBuildJob(jobId);
            if (buildJob != null)
            {
                if (buildJob.State == BuildJobState.Building)
                    return new ErrorResponse(
                        "Cannot cancel a single build in progress. BuildPipeline.BuildPlayer is " +
                        "synchronous and blocks the editor until completion.");
                return new ErrorResponse($"Build is already {buildJob.State}.");
            }

            return new ErrorResponse($"No job found with ID: {jobId}");
        }
    }
}
