using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace Project.Tasks.Editor
{
    internal sealed class TaskBoardWindow : EditorWindow
    {
        private const float ColumnWidth = 290f;
        private const float DefaultDetailsWidth = 420f;
        private const float MinDetailsWidth = 320f;
        private const float MinBoardWidth = 420f;
        private const float SplitterWidth = 5f;
        private const float MinButtonSize = 9f;
        private const string CustomOwnerOption = "<Custom>";
        private const string DetailsWidthPrefsKey = "Project.Tasks.Editor.TaskBoardWindow.DetailsWidth";
        private const double DeleteConfirmTimeoutSeconds = 3.0;

        private TaskBoardData data;
        private Vector2 boardScroll;
        private Vector2 detailsScroll;
        private string searchText = string.Empty;
        private string sortOption = "Priority Desc";
        private bool filtersExpanded = true;

        private readonly HashSet<string> selectedStatuses = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private readonly HashSet<string> selectedPriorities = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private readonly HashSet<string> selectedAreas = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private readonly HashSet<string> selectedOwners = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private readonly HashSet<string> selectedFileStates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private readonly HashSet<string> selectedWarningStates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        private TaskRecord selectedTask;
        private string saveMessage = string.Empty;
        private MessageType saveMessageType = MessageType.Info;

        private string editStatus;
        private string editPriority;
        private string editOwner;
        private bool editOwnerIsCustom;
        private string editExecutionTime;
        private string editIndexSummary;
        private string editTaskSummary;
        private string editWhy;
        private string editExpectedOutcome;
        private string editCurrentContext;
        private string editAcceptanceCriteria;
        private string editVerification;
        private string editRisks;
        private string editHumanDecisions;
        private string editDecisionLog;
        private string editHandoffNotes;

        private GUIStyle readOnlyWrappedTextArea;
        private GUIStyle priorityBadgeStyle;
        private GUIStyle compactButtonStyle;
        private TaskRecord pressedTask;
        private Vector2 pressedMousePosition;
        private float detailsWidth = DefaultDetailsWidth;
        private bool isResizingDetails;
        private string pendingDeleteTaskId;
        private double pendingDeleteUntil;

        [MenuItem("Tools/Tasks/Kanban Board")]
        public static void Open()
        {
            GetWindow<TaskBoardWindow>("Task Board");
        }

        private void OnEnable()
        {
            detailsWidth = EditorPrefs.GetFloat(DetailsWidthPrefsKey, DefaultDetailsWidth);
            Reload();
        }

        private void OnDisable()
        {
            EditorPrefs.SetFloat(DetailsWidthPrefsKey, detailsWidth);
        }

        private void OnGUI()
        {
            ResetDeleteConfirmationIfExpired();
            DrawToolbar();

            if (data == null)
            {
                EditorGUILayout.HelpBox("Данные задач еще не загружены.", MessageType.Info);
                return;
            }

            if (data.Warnings.Count > 0)
            {
                EditorGUILayout.HelpBox(string.Join("\n", data.Warnings), MessageType.Warning);
            }

            if (!string.IsNullOrEmpty(saveMessage))
            {
                EditorGUILayout.HelpBox(saveMessage, saveMessageType);
            }

            DrawSummaryBar();
            DrawFilterPanel();

            float clampedDetailsWidth = Mathf.Clamp(detailsWidth, MinDetailsWidth, Mathf.Max(MinDetailsWidth, position.width - MinBoardWidth));
            if (!Mathf.Approximately(clampedDetailsWidth, detailsWidth))
            {
                detailsWidth = clampedDetailsWidth;
            }

            EditorGUILayout.BeginHorizontal();
            DrawBoard(position.width - detailsWidth - SplitterWidth - 24f);
            DrawDetailsSplitter();
            DrawDetails(detailsWidth);
            EditorGUILayout.EndHorizontal();
        }

        private void DrawToolbar()
        {
            EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);

            if (GUILayout.Button("Reload", EditorStyles.toolbarButton))
            {
                Reload(selectedTask != null ? selectedTask.Id : null);
            }

            if (GUILayout.Button("Index", EditorStyles.toolbarButton))
            {
                string indexPath = data != null ? data.IndexPath : TaskBoardService.NormalizePath(System.IO.Path.Combine(TaskBoardService.GetProjectRoot(), "docs", "tasks", "Index.md"));
                TaskBoardService.OpenInDefaultApp(indexPath);
            }

            if (GUILayout.Button("Owners", EditorStyles.toolbarButton))
            {
                OpenOwnersConfig();
            }

            GUILayout.Space(8f);
            GUILayout.Label("Search", GUILayout.Width(45f));
            searchText = GUILayout.TextField(searchText, GUI.skin.FindStyle("ToolbarSeachTextField") ?? EditorStyles.toolbarTextField, GUILayout.MinWidth(180f));

            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();
        }

        private void DrawSummaryBar()
        {
            if (data == null)
            {
                return;
            }

            List<TaskRecord> filteredTasks = GetFilteredTasks();
            int totalMinutes = TaskBoardService.SumEstimatedMinutes(filteredTasks);
            int warnings = filteredTasks.Count(task => task.ValidationMessages.Count > 0);
            int missingFiles = filteredTasks.Count(task => !task.FileExists);

            EditorGUILayout.BeginVertical("box");
            EditorGUILayout.LabelField(
                "Visible Tasks: " + filteredTasks.Count + " / " + data.Tasks.Count
                + " | Visible Time: " + TaskBoardService.FormatMinutesForDisplay(totalMinutes)
                + " | Warnings: " + warnings
                + " | Missing Files: " + missingFiles,
                EditorStyles.wordWrappedMiniLabel);

            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Sort", GUILayout.Width(32f));
            sortOption = DrawPopup(sortOption, BuildSortOptions(), 160f);
            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
        }

        private void DrawFilterPanel()
        {
            EditorGUILayout.BeginVertical("box");
            filtersExpanded = EditorGUILayout.Foldout(filtersExpanded, "Filters", true);
            if (filtersExpanded)
            {
                DrawSelectionGroup("Statuses", TaskBoardConstants.Statuses, selectedStatuses);
                DrawSelectionGroup("Priorities", TaskBoardConstants.Priorities, selectedPriorities);
                DrawSelectionGroup("Areas", BuildAreaOptions(), selectedAreas);
                DrawSelectionGroup("Owners", BuildOwnerFilterOptions(), selectedOwners);
                DrawSelectionGroup("Files", new[] { "Existing", "Missing" }, selectedFileStates);
                DrawSelectionGroup("Warnings", new[] { "Warnings", "Clean" }, selectedWarningStates);
            }
            EditorGUILayout.EndVertical();
        }

        private void DrawSelectionGroup(string label, string[] options, HashSet<string> selectedValues)
        {
            if (options == null || options.Length == 0)
            {
                return;
            }

            GUIStyle buttonStyle = GetCompactButtonStyle();

            EditorGUILayout.BeginVertical("box");
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(label, EditorStyles.boldLabel);
            if (GUILayout.Button("All", buttonStyle))
            {
                SetAll(selectedValues, options, true);
            }

            if (GUILayout.Button("Nothing", buttonStyle))
            {
                selectedValues.Clear();
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            for (int i = 0; i < options.Length; i++)
            {
                string option = options[i];
                bool current = selectedValues.Contains(option);
                bool next = GUILayout.Toggle(current, option, buttonStyle);
                if (next != current)
                {
                    if (next)
                    {
                        selectedValues.Add(option);
                    }
                    else
                    {
                        selectedValues.Remove(option);
                    }
                }

                if ((i + 1) % 4 == 0)
                {
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.BeginHorizontal();
                }
            }
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
        }

        private void DrawBoard(float width)
        {
            List<TaskRecord> filteredTasks = GetFilteredTasks();

            EditorGUILayout.BeginVertical(GUILayout.Width(Mathf.Max(MinBoardWidth, width)), GUILayout.ExpandHeight(true));
            boardScroll = EditorGUILayout.BeginScrollView(boardScroll, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
            EditorGUILayout.BeginHorizontal();

            foreach (string status in TaskBoardConstants.Statuses)
            {
                DrawColumn(status, filteredTasks.Where(task => string.Equals(task.Status, status, StringComparison.OrdinalIgnoreCase)).ToList());
            }

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndScrollView();
            EditorGUILayout.EndVertical();
        }

        private void DrawColumn(string status, List<TaskRecord> tasks)
        {
            EditorGUILayout.BeginVertical("box", GUILayout.Width(ColumnWidth), GUILayout.ExpandHeight(true));
            EditorGUILayout.LabelField(status, EditorStyles.boldLabel);
            EditorGUILayout.LabelField(tasks.Count + " task(s)", EditorStyles.miniLabel);
            EditorGUILayout.LabelField(TaskBoardService.FormatMinutesForDisplay(TaskBoardService.SumEstimatedMinutes(tasks)), EditorStyles.miniLabel);
            EditorGUILayout.Space(6f);

            Rect headerRect = GUILayoutUtility.GetLastRect();
            Rect lastRect = headerRect;

            if (tasks.Count == 0)
            {
                EditorGUILayout.HelpBox("Нет задач по текущему фильтру.", MessageType.None);
                lastRect = GUILayoutUtility.GetLastRect();
            }

            foreach (TaskRecord task in tasks)
            {
                DrawTaskCard(task);
                lastRect = GUILayoutUtility.GetLastRect();
                EditorGUILayout.Space(6f);
            }

            GUILayout.FlexibleSpace();
            EditorGUILayout.EndVertical();

            Rect columnRect = new Rect(headerRect.xMin - 6f, headerRect.yMin - 28f, ColumnWidth, Mathf.Max(110f, lastRect.yMax - headerRect.yMin + 36f));
            HandleColumnDrop(columnRect, status);
        }

        private void DrawTaskCard(TaskRecord task)
        {
            Color previousBackground = GUI.backgroundColor;
            GUI.backgroundColor = selectedTask == task ? new Color(0.72f, 0.82f, 1f) : GetStatusColor(task.Status);

            EditorGUILayout.BeginVertical("box");
            GUI.backgroundColor = previousBackground;

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(task.Id, EditorStyles.boldLabel);
            GUILayout.FlexibleSpace();
            DrawPriorityBadge(task.Priority);
            EditorGUILayout.EndHorizontal();
            Rect firstRect = GUILayoutUtility.GetLastRect();

            EditorGUILayout.LabelField(string.IsNullOrEmpty(task.Title) ? task.Id : task.Title, EditorStyles.wordWrappedLabel);
            if (!string.IsNullOrEmpty(task.IndexSummary))
            {
                EditorGUILayout.LabelField(task.IndexSummary, EditorStyles.wordWrappedMiniLabel);
            }

            EditorGUILayout.Space(4f);
            EditorGUILayout.LabelField("Area: " + SafeValue(task.Area) + "   Owner: " + SafeValue(task.Owner), EditorStyles.miniLabel);
            EditorGUILayout.LabelField("Time: " + GetFormattedTime(task), EditorStyles.miniLabel);

            if (task.ValidationMessages.Count > 0)
            {
                EditorGUILayout.HelpBox(task.ValidationMessages[0], MessageType.Warning);
            }

            if (GUILayout.Button("Details", GetCompactButtonStyle()))
            {
                SelectTask(task);
            }

            EditorGUILayout.EndVertical();

            Rect lastRect = GUILayoutUtility.GetLastRect();
            Rect cardRect = new Rect(firstRect.xMin - 6f, firstRect.yMin - 6f, firstRect.width + 12f, Mathf.Max(44f, lastRect.yMax - firstRect.yMin + 12f));
            HandleCardDrag(task, cardRect);
        }

        private void DrawDetails(float width)
        {
            EditorGUILayout.BeginVertical("box", GUILayout.Width(width), GUILayout.ExpandHeight(true));
            EditorGUILayout.LabelField("Task Details", EditorStyles.boldLabel);

            if (selectedTask == null)
            {
                EditorGUILayout.HelpBox("Выберите карточку, чтобы посмотреть детали задачи и изменить статус.", MessageType.Info);
                EditorGUILayout.EndVertical();
                return;
            }

            detailsScroll = EditorGUILayout.BeginScrollView(detailsScroll, GUILayout.ExpandHeight(true));
            EditorGUILayout.LabelField(selectedTask.Id, EditorStyles.boldLabel);
            EditorGUILayout.LabelField(string.IsNullOrEmpty(selectedTask.Title) ? selectedTask.Id : selectedTask.Title, EditorStyles.wordWrappedLabel);
            EditorGUILayout.Space(8f);

            if (selectedTask.ValidationMessages.Count > 0)
            {
                EditorGUILayout.HelpBox(string.Join("\n", selectedTask.ValidationMessages), MessageType.Warning);
            }

            if (!selectedTask.FileExists)
            {
                EditorGUILayout.HelpBox("Task-файл отсутствует. Поля из task-файла недоступны до создания файла по шаблону.", MessageType.Info);
            }

            GUIStyle btn = GetCompactButtonStyle();

            EditorGUILayout.BeginHorizontal();
            using (new EditorGUI.DisabledScope(!selectedTask.FileExists))
            {
                if (GUILayout.Button("File", btn, GUILayout.Width(75f), GUILayout.Height(18f)))
                {
                    TaskBoardService.OpenInDefaultApp(selectedTask.AbsoluteFilePath);
                }
            }

            using (new EditorGUI.DisabledScope(selectedTask.FileExists))
            {
                if (GUILayout.Button("Create", btn, GUILayout.Width(75f), GUILayout.Height(18f)))
                {
                    CreateSelectedTaskFromTemplate();
                }
            }

            if (GUILayout.Button("Index", btn, GUILayout.Width(75f), GUILayout.Height(18f)))
            {
                TaskBoardService.OpenInDefaultApp(data.IndexPath);
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space(4f);
            DrawDeleteButton();

            EditorGUILayout.Space(8f);
            DrawEditableFields();
            EditorGUILayout.Space(12f);

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Priority", GUILayout.Width(80f));
            DrawPriorityBadge(selectedTask.Priority);
            EditorGUILayout.EndHorizontal();
            DrawReadOnlyField("Estimate", GetFormattedTime(selectedTask));
            DrawReadOnlyField("File", selectedTask.RelativeFilePath);
            DrawReadOnlyField("Created", selectedTask.Created);
            DrawReadOnlyField("Updated", selectedTask.Updated);
            DrawReadOnlyField("Area", selectedTask.Area);

            EditorGUILayout.Space(10f);
            EditorGUILayout.LabelField("Task File Sections", EditorStyles.boldLabel);
            DrawEditableSection("Why", ref editWhy, selectedTask.FileExists);
            DrawEditableSection("Expected Outcome", ref editExpectedOutcome, selectedTask.FileExists);
            DrawEditableSection("Current Context", ref editCurrentContext, selectedTask.FileExists);
            DrawEditableSection("Acceptance Criteria", ref editAcceptanceCriteria, selectedTask.FileExists);
            DrawEditableSection("Verification", ref editVerification, selectedTask.FileExists);
            DrawEditableSection("Risks / Open Questions", ref editRisks, selectedTask.FileExists);
            DrawEditableSection("Human Decisions Needed", ref editHumanDecisions, selectedTask.FileExists);
            DrawEditableSection("Decision Log", ref editDecisionLog, selectedTask.FileExists);
            DrawEditableSection("Handoff Notes", ref editHandoffNotes, selectedTask.FileExists);

            EditorGUILayout.EndScrollView();
            EditorGUILayout.EndVertical();
        }

        private void DrawEditableFields()
        {
            EditorGUILayout.LabelField("Index Fields", EditorStyles.boldLabel);
            editStatus = DrawStringPopup("Status", editStatus, TaskBoardConstants.Statuses);
            editPriority = DrawStringPopup("Priority", editPriority, TaskBoardConstants.Priorities);
            DrawOwnerField();
            editExecutionTime = EditorGUILayout.TextField("Execution Time", editExecutionTime ?? string.Empty);

            string normalizedExecutionTime;
            int estimatedMinutes;
            if (TaskBoardService.TryNormalizeExecutionTime(editExecutionTime, out normalizedExecutionTime, out estimatedMinutes))
            {
                editExecutionTime = normalizedExecutionTime;
            }

            EditorGUILayout.LabelField("Estimate", TaskBoardService.IsValidExecutionTime(editExecutionTime)
                ? TaskBoardService.FormatExecutionTimeForDisplay(editExecutionTime)
                : "-");

            EditorGUILayout.LabelField("Index Summary");
            editIndexSummary = EditorGUILayout.TextArea(editIndexSummary ?? string.Empty, GUILayout.MinHeight(60f));

            EditorGUILayout.Space(8f);
            EditorGUILayout.LabelField("Task File Fields", EditorStyles.boldLabel);
            using (new EditorGUI.DisabledScope(!selectedTask.FileExists))
            {
                EditorGUILayout.LabelField("Task Summary");
                editTaskSummary = EditorGUILayout.TextArea(editTaskSummary ?? string.Empty, GUILayout.MinHeight(60f));
            }

            bool hasChanges = HasChanges();
            bool executionTimeValid = TaskBoardService.IsValidExecutionTime(editExecutionTime);
            if (!executionTimeValid)
            {
                EditorGUILayout.HelpBox("execution_time должен быть в формате Jira, например 1d6h30m, и быть кратным 30 минутам.", MessageType.Warning);
            }

            using (new EditorGUI.DisabledScope(!hasChanges || !executionTimeValid))
            {
                if (GUILayout.Button("Save", GetCompactButtonStyle()))
                {
                    SaveSelectedTask();
                }
            }
        }

        private void DrawDeleteButton()
        {
            if (selectedTask == null)
            {
                return;
            }

            bool isPending = string.Equals(pendingDeleteTaskId, selectedTask.Id, StringComparison.OrdinalIgnoreCase) && EditorApplication.timeSinceStartup <= pendingDeleteUntil;
            Color previous = GUI.backgroundColor;
            GUI.backgroundColor = isPending ? new Color(0.85f, 0.3f, 0.3f) : new Color(0.7f, 0.25f, 0.25f);
            if (GUILayout.Button(isPending ? "Sure?" : "Delete Task", GetCompactButtonStyle(), GUILayout.Width(75f), GUILayout.Height(18f)))
            {
                if (isPending)
                {
                    DeleteSelectedTask();
                }
                else
                {
                    pendingDeleteTaskId = selectedTask.Id;
                    pendingDeleteUntil = EditorApplication.timeSinceStartup + DeleteConfirmTimeoutSeconds;
                }
            }
            GUI.backgroundColor = previous;
        }

        private string DrawStringPopup(string label, string currentValue, string[] options)
        {
            int index = Array.IndexOf(options, currentValue);
            if (index < 0)
            {
                index = 0;
            }

            int nextIndex = EditorGUILayout.Popup(label, index, options);
            return options[Mathf.Clamp(nextIndex, 0, options.Length - 1)];
        }

        private string DrawPopup(string currentValue, string[] options, float width)
        {
            int index = Array.IndexOf(options, currentValue);
            if (index < 0)
            {
                index = 0;
            }

            int nextIndex = EditorGUILayout.Popup(index, options, EditorStyles.toolbarPopup, GUILayout.Width(width));
            return options[Mathf.Clamp(nextIndex, 0, options.Length - 1)];
        }

        private void DrawReadOnlyField(string label, string value)
        {
            EditorGUILayout.LabelField(label, string.IsNullOrEmpty(value) ? "-" : value);
        }

        private void DrawEditableSection(string title, ref string content, bool enabled)
        {
            EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
            using (new EditorGUI.DisabledScope(!enabled))
            {
                content = EditorGUILayout.TextArea(enabled ? (content ?? string.Empty) : string.Empty, GetReadOnlyWrappedTextArea(), GUILayout.MinHeight(52f));
            }
            EditorGUILayout.Space(6f);
        }

        private GUIStyle GetReadOnlyWrappedTextArea()
        {
            if (readOnlyWrappedTextArea == null)
            {
                readOnlyWrappedTextArea = new GUIStyle(EditorStyles.textArea) { wordWrap = true };
            }

            return readOnlyWrappedTextArea;
        }

        private GUIStyle GetPriorityBadgeStyle()
        {
            if (priorityBadgeStyle == null)
            {
                priorityBadgeStyle = new GUIStyle(EditorStyles.miniBoldLabel)
                {
                    alignment = TextAnchor.MiddleCenter,
                    padding = new RectOffset(8, 8, 3, 3),
                    normal = { textColor = Color.white },
                };
            }

            return priorityBadgeStyle;
        }

        private GUIStyle GetCompactButtonStyle()
        {
            if (compactButtonStyle == null)
            {
                compactButtonStyle = new GUIStyle("Button")
                {
                    alignment = TextAnchor.MiddleCenter,
                    padding = new RectOffset(4, 4, 2, 2),
                    margin = new RectOffset(1, 1, 1, 1),
                    stretchWidth = true,
                };
            }

            return compactButtonStyle;
        }

        private void DrawPriorityBadge(string priority)
        {
            GUIStyle style = GetPriorityBadgeStyle();
            Vector2 contentSize = style.CalcSize(new GUIContent(priority));
            Rect rect = GUILayoutUtility.GetRect(contentSize.x + 18f, 22f, GUILayout.Width(contentSize.x + 18f), GUILayout.Height(22f));
            EditorGUI.DrawRect(rect, GetPriorityColor(priority));
            GUI.Label(rect, priority, style);
        }

        private void DrawOwnerField()
        {
            string[] presetOptions = BuildOwnerPresetOptions();
            int currentIndex = GetOwnerPopupIndex(presetOptions);
            int nextIndex = EditorGUILayout.Popup("Owner", currentIndex, presetOptions);

            if (nextIndex >= 0 && nextIndex < presetOptions.Length)
            {
                string selectedValue = presetOptions[nextIndex];
                if (string.Equals(selectedValue, CustomOwnerOption, StringComparison.Ordinal))
                {
                    editOwnerIsCustom = true;
                }
                else
                {
                    editOwnerIsCustom = false;
                    editOwner = selectedValue;
                }
            }

            if (editOwnerIsCustom)
            {
                editOwner = EditorGUILayout.TextField("Custom Owner", editOwner ?? string.Empty);
            }
        }

        private void HandleCardDrag(TaskRecord task, Rect cardRect)
        {
            Event current = Event.current;
            if (current.type == EventType.MouseDown && current.button == 0 && cardRect.Contains(current.mousePosition))
            {
                pressedTask = task;
                pressedMousePosition = current.mousePosition;
            }

            if (pressedTask != task)
            {
                return;
            }

            if (current.type == EventType.MouseDrag && (current.mousePosition - pressedMousePosition).sqrMagnitude > 16f)
            {
                DragAndDrop.PrepareStartDrag();
                DragAndDrop.objectReferences = Array.Empty<UnityEngine.Object>();
                DragAndDrop.SetGenericData("TaskBoard.TaskId", task.Id);
                DragAndDrop.StartDrag(task.Id);
                current.Use();
            }

            if (current.type == EventType.MouseUp)
            {
                pressedTask = null;
            }
        }

        private void HandleColumnDrop(Rect columnRect, string status)
        {
            Event current = Event.current;
            if (!columnRect.Contains(current.mousePosition))
            {
                return;
            }

            if (current.type == EventType.DragUpdated)
            {
                if (GetDraggedTask() != null)
                {
                    DragAndDrop.visualMode = DragAndDropVisualMode.Move;
                    current.Use();
                    Repaint();
                }
                return;
            }

            if (current.type == EventType.DragPerform)
            {
                TaskRecord task = GetDraggedTask();
                if (task == null)
                {
                    return;
                }

                DragAndDrop.AcceptDrag();
                current.Use();
                pressedTask = null;

                if (!string.Equals(task.Status, status, StringComparison.Ordinal))
                {
                    MoveTaskToStatus(task, status);
                }
            }
        }

        private TaskRecord GetDraggedTask()
        {
            string draggedTaskId = DragAndDrop.GetGenericData("TaskBoard.TaskId") as string;
            if (string.IsNullOrEmpty(draggedTaskId) || data == null)
            {
                return null;
            }

            return data.Tasks.FirstOrDefault(task => string.Equals(task.Id, draggedTaskId, StringComparison.OrdinalIgnoreCase));
        }

        private void MoveTaskToStatus(TaskRecord task, string status)
        {
            string selectedId = task.Id;
            task.Status = status;

            string error;
            if (!TaskBoardService.Save(data, task, out error))
            {
                saveMessage = "Не удалось изменить статус перетаскиванием: " + error;
                saveMessageType = MessageType.Error;
                return;
            }

            saveMessage = task.FileExists
                ? "Статус задачи обновлен перетаскиванием."
                : "Статус задачи обновлен в Index.md. Task-файл не найден, поэтому поля task-файла не обновлялись.";
            saveMessageType = MessageType.Info;
            Reload(selectedId);
        }

        private void SelectTask(TaskRecord task)
        {
            ResetDeleteConfirmation();
            selectedTask = task;
            TaskBoardService.LoadTaskDetails(selectedTask);
            editStatus = selectedTask.Status;
            editPriority = selectedTask.Priority;
            editOwner = selectedTask.Owner;
            editOwnerIsCustom = data != null && !data.OwnerPresets.Any(owner => string.Equals(owner, selectedTask.Owner, StringComparison.OrdinalIgnoreCase));
            editExecutionTime = TaskBoardService.NormalizeExecutionTime(selectedTask.ExecutionTime);
            editIndexSummary = selectedTask.IndexSummary;
            editTaskSummary = selectedTask.TaskSummary;
            editWhy = selectedTask.Why;
            editExpectedOutcome = selectedTask.ExpectedOutcome;
            editCurrentContext = selectedTask.CurrentContext;
            editAcceptanceCriteria = selectedTask.AcceptanceCriteria;
            editVerification = selectedTask.Verification;
            editRisks = selectedTask.Risks;
            editHumanDecisions = selectedTask.HumanDecisions;
            editDecisionLog = selectedTask.DecisionLog;
            editHandoffNotes = selectedTask.HandoffNotes;
        }

        private void SaveSelectedTask()
        {
            if (selectedTask == null)
            {
                return;
            }

            string selectedId = selectedTask.Id;
            selectedTask.Status = TaskBoardService.NormalizeStatus(editStatus);
            selectedTask.Priority = editPriority;
            selectedTask.Owner = string.IsNullOrWhiteSpace(editOwner) ? "unassigned" : editOwner.Trim();
            selectedTask.ExecutionTime = TaskBoardService.NormalizeExecutionTime(editExecutionTime);
            selectedTask.IndexSummary = (editIndexSummary ?? string.Empty).Trim();
            if (selectedTask.FileExists)
            {
                selectedTask.TaskSummary = (editTaskSummary ?? string.Empty).Trim();
                selectedTask.Why = (editWhy ?? string.Empty).Trim();
                selectedTask.ExpectedOutcome = (editExpectedOutcome ?? string.Empty).Trim();
                selectedTask.CurrentContext = (editCurrentContext ?? string.Empty).Trim();
                selectedTask.AcceptanceCriteria = (editAcceptanceCriteria ?? string.Empty).Trim();
                selectedTask.Verification = (editVerification ?? string.Empty).Trim();
                selectedTask.Risks = (editRisks ?? string.Empty).Trim();
                selectedTask.HumanDecisions = (editHumanDecisions ?? string.Empty).Trim();
                selectedTask.DecisionLog = (editDecisionLog ?? string.Empty).Trim();
                selectedTask.HandoffNotes = (editHandoffNotes ?? string.Empty).Trim();
            }

            string error;
            if (!TaskBoardService.Save(data, selectedTask, out error))
            {
                saveMessage = "Не удалось сохранить задачу: " + error;
                saveMessageType = MessageType.Error;
                return;
            }

            saveMessage = selectedTask.FileExists
                ? "Изменения сохранены в Index.md и task-файл."
                : "Изменения сохранены в Index.md. Task-файл не найден, поэтому поля task-файла не обновлялись.";
            saveMessageType = MessageType.Info;
            Reload(selectedId);
        }

        private void CreateSelectedTaskFromTemplate()
        {
            if (selectedTask == null)
            {
                return;
            }

            string selectedId = selectedTask.Id;
            string error;
            if (!TaskBoardService.CreateTaskFileFromTemplate(data, selectedTask, out error))
            {
                saveMessage = "Не удалось создать task-файл из шаблона: " + error;
                saveMessageType = MessageType.Error;
                return;
            }

            saveMessage = "Task-файл создан по шаблону.";
            saveMessageType = MessageType.Info;
            Reload(selectedId);
        }

        private void DeleteSelectedTask()
        {
            if (selectedTask == null)
            {
                return;
            }

            string deletedId = selectedTask.Id;
            string error;
            if (!TaskBoardService.DeleteTask(data, selectedTask, out error))
            {
                saveMessage = "Не удалось удалить задачу: " + error;
                saveMessageType = MessageType.Error;
                return;
            }

            ResetDeleteConfirmation();
            saveMessage = "Задача " + deletedId + " удалена.";
            saveMessageType = MessageType.Info;
            selectedTask = null;
            Reload();
        }

        private bool HasChanges()
        {
            if (selectedTask == null)
            {
                return false;
            }

            bool taskSummaryChanged = selectedTask.FileExists && !string.Equals(selectedTask.TaskSummary ?? string.Empty, editTaskSummary ?? string.Empty, StringComparison.Ordinal);
            bool taskBodyChanged = selectedTask.FileExists
                && (!string.Equals(selectedTask.Why ?? string.Empty, editWhy ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.ExpectedOutcome ?? string.Empty, editExpectedOutcome ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.CurrentContext ?? string.Empty, editCurrentContext ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.AcceptanceCriteria ?? string.Empty, editAcceptanceCriteria ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.Verification ?? string.Empty, editVerification ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.Risks ?? string.Empty, editRisks ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.HumanDecisions ?? string.Empty, editHumanDecisions ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.DecisionLog ?? string.Empty, editDecisionLog ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.HandoffNotes ?? string.Empty, editHandoffNotes ?? string.Empty, StringComparison.Ordinal));
            return !string.Equals(selectedTask.Status, editStatus, StringComparison.Ordinal)
                || !string.Equals(selectedTask.Priority, editPriority, StringComparison.Ordinal)
                || !string.Equals(selectedTask.Owner ?? string.Empty, editOwner ?? string.Empty, StringComparison.Ordinal)
                || !string.Equals(selectedTask.ExecutionTime ?? string.Empty, TaskBoardService.NormalizeExecutionTime(editExecutionTime), StringComparison.Ordinal)
                || !string.Equals(selectedTask.IndexSummary ?? string.Empty, editIndexSummary ?? string.Empty, StringComparison.Ordinal)
                || taskSummaryChanged
                || taskBodyChanged;
        }

        private void Reload(string selectedId = null)
        {
            data = TaskBoardService.Load();
            SyncSelections(selectedStatuses, TaskBoardConstants.Statuses);
            SyncSelections(selectedPriorities, TaskBoardConstants.Priorities);
            SyncSelections(selectedAreas, BuildAreaOptions());
            SyncSelections(selectedOwners, BuildOwnerFilterOptions());
            SyncSelections(selectedFileStates, new[] { "Existing", "Missing" });
            SyncSelections(selectedWarningStates, new[] { "Warnings", "Clean" });

            if (data == null || data.Tasks.Count == 0)
            {
                ResetDeleteConfirmation();
                selectedTask = null;
                return;
            }

            TaskRecord nextSelection = null;
            if (!string.IsNullOrEmpty(selectedId))
            {
                nextSelection = data.Tasks.FirstOrDefault(task => string.Equals(task.Id, selectedId, StringComparison.OrdinalIgnoreCase));
            }

            if (nextSelection == null && selectedTask != null)
            {
                nextSelection = data.Tasks.FirstOrDefault(task => string.Equals(task.Id, selectedTask.Id, StringComparison.OrdinalIgnoreCase));
            }

            if (nextSelection == null)
            {
                selectedTask = null;
                editStatus = string.Empty;
                editPriority = string.Empty;
                editOwner = string.Empty;
                editExecutionTime = string.Empty;
                editIndexSummary = string.Empty;
                editTaskSummary = string.Empty;
                editWhy = string.Empty;
                editExpectedOutcome = string.Empty;
                editCurrentContext = string.Empty;
                editAcceptanceCriteria = string.Empty;
                editVerification = string.Empty;
                editRisks = string.Empty;
                editHumanDecisions = string.Empty;
                editDecisionLog = string.Empty;
                editHandoffNotes = string.Empty;
                return;
            }

            SelectTask(nextSelection);
        }

        private List<TaskRecord> GetFilteredTasks()
        {
            IEnumerable<TaskRecord> query = data != null ? data.Tasks : Enumerable.Empty<TaskRecord>();

            if (!string.IsNullOrWhiteSpace(searchText))
            {
                string search = searchText.Trim();
                query = query.Where(task =>
                    ContainsIgnoreCase(task.Id, search)
                    || ContainsIgnoreCase(task.Title, search)
                    || ContainsIgnoreCase(task.IndexSummary, search)
                    || ContainsIgnoreCase(task.Area, search)
                    || ContainsIgnoreCase(task.Owner, search));
            }

            query = query.Where(task => selectedStatuses.Contains(task.Status));
            query = query.Where(task => selectedPriorities.Contains(task.Priority));
            query = query.Where(task => selectedAreas.Contains(string.IsNullOrWhiteSpace(task.Area) ? "-" : task.Area));
            query = query.Where(task => selectedOwners.Contains(string.IsNullOrWhiteSpace(task.Owner) ? "unassigned" : task.Owner));
            query = query.Where(task => selectedFileStates.Contains(task.FileExists ? "Existing" : "Missing"));
            query = query.Where(task => selectedWarningStates.Contains(task.ValidationMessages.Count > 0 ? "Warnings" : "Clean"));

            return ApplySort(query).ToList();
        }

        private string[] BuildAreaOptions()
        {
            if (data == null)
            {
                return Array.Empty<string>();
            }

            return data.Tasks
                .Select(task => string.IsNullOrWhiteSpace(task.Area) ? "-" : task.Area)
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .OrderBy(area => area, StringComparer.OrdinalIgnoreCase)
                .ToArray();
        }

        private string[] BuildOwnerFilterOptions()
        {
            return TaskBoardService.BuildOwnerOptions(data, data != null ? data.Tasks : null);
        }

        private string[] BuildOwnerPresetOptions()
        {
            var options = new List<string>();
            if (data != null)
            {
                options.AddRange(data.OwnerPresets.Where(owner => !string.IsNullOrWhiteSpace(owner)));
            }

            if (!string.IsNullOrWhiteSpace(editOwner) && !options.Any(owner => string.Equals(owner, editOwner, StringComparison.OrdinalIgnoreCase)) && !editOwnerIsCustom)
            {
                options.Insert(0, editOwner);
            }

            options.Add(CustomOwnerOption);
            return options.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
        }

        private string[] BuildSortOptions()
        {
            return new[]
            {
                "Priority Desc",
                "Priority Asc",
                "Execution Time Desc",
                "Execution Time Asc",
                "ID Asc",
                "ID Desc",
                "Owner Asc",
                "Title Asc",
                "Area Asc",
            };
        }

        private IEnumerable<TaskRecord> ApplySort(IEnumerable<TaskRecord> tasks)
        {
            switch (sortOption)
            {
                case "Priority Asc":
                    return tasks.OrderByDescending(task => TaskBoardService.GetPrioritySortOrder(task.Priority)).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "Execution Time Desc":
                    return tasks.OrderByDescending(task => task.EstimatedMinutes).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "Execution Time Asc":
                    return tasks.OrderBy(task => task.EstimatedMinutes < 0 ? int.MaxValue : task.EstimatedMinutes).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "ID Desc":
                    return tasks.OrderByDescending(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "Owner Asc":
                    return tasks.OrderBy(task => task.Owner, StringComparer.OrdinalIgnoreCase).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "Title Asc":
                    return tasks.OrderBy(task => task.Title, StringComparer.OrdinalIgnoreCase).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "Area Asc":
                    return tasks.OrderBy(task => task.Area, StringComparer.OrdinalIgnoreCase).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "ID Asc":
                    return tasks.OrderBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
                case "Priority Desc":
                default:
                    return tasks.OrderBy(task => TaskBoardService.GetPrioritySortOrder(task.Priority)).ThenBy(task => task.Id, StringComparer.OrdinalIgnoreCase);
            }
        }

        private void DrawDetailsSplitter()
        {
            Rect splitterRect = GUILayoutUtility.GetRect(SplitterWidth, 1f, GUILayout.Width(SplitterWidth), GUILayout.ExpandHeight(true));
            EditorGUIUtility.AddCursorRect(splitterRect, MouseCursor.ResizeHorizontal);
            EditorGUI.DrawRect(splitterRect, new Color(0.2f, 0.2f, 0.2f, 0.75f));

            Event current = Event.current;
            switch (current.type)
            {
                case EventType.MouseDown:
                    if (splitterRect.Contains(current.mousePosition))
                    {
                        isResizingDetails = true;
                        current.Use();
                    }
                    break;
                case EventType.MouseDrag:
                    if (isResizingDetails)
                    {
                        detailsWidth = Mathf.Clamp(position.width - current.mousePosition.x, MinDetailsWidth, Mathf.Max(MinDetailsWidth, position.width - MinBoardWidth));
                        Repaint();
                        current.Use();
                    }
                    break;
                case EventType.MouseUp:
                    if (isResizingDetails)
                    {
                        isResizingDetails = false;
                        EditorPrefs.SetFloat(DetailsWidthPrefsKey, detailsWidth);
                        current.Use();
                    }
                    break;
            }
        }

        private void OpenOwnersConfig()
        {
            if (data == null || string.IsNullOrWhiteSpace(data.OwnersConfigPath))
            {
                return;
            }

            UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(data.OwnersConfigPath);
            if (asset != null)
            {
                Selection.activeObject = asset;
                EditorGUIUtility.PingObject(asset);
            }
        }

        private int GetOwnerPopupIndex(string[] presetOptions)
        {
            if (editOwnerIsCustom)
            {
                return Array.IndexOf(presetOptions, CustomOwnerOption);
            }

            int index = Array.FindIndex(presetOptions, owner => string.Equals(owner, editOwner, StringComparison.OrdinalIgnoreCase));
            return index >= 0 ? index : Array.IndexOf(presetOptions, CustomOwnerOption);
        }

        private string GetFormattedTime(TaskRecord task)
        {
            return task == null ? "-" : TaskBoardService.FormatExecutionTimeForDisplay(task.ExecutionTime);
        }

        private void SyncSelections(HashSet<string> selectedValues, string[] availableOptions)
        {
            var availableSet = new HashSet<string>(availableOptions ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
            selectedValues.RemoveWhere(value => !availableSet.Contains(value));
            if (selectedValues.Count == 0)
            {
                SetAll(selectedValues, availableOptions, true);
            }
        }

        private void SetAll(HashSet<string> selectedValues, string[] values, bool enabled)
        {
            selectedValues.Clear();
            if (!enabled || values == null)
            {
                return;
            }

            foreach (string value in values)
            {
                selectedValues.Add(value);
            }
        }

        private static bool ContainsIgnoreCase(string source, string value)
        {
            return !string.IsNullOrEmpty(source) && source.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
        }

        private void ResetDeleteConfirmationIfExpired()
        {
            if (!string.IsNullOrEmpty(pendingDeleteTaskId) && EditorApplication.timeSinceStartup > pendingDeleteUntil)
            {
                ResetDeleteConfirmation();
            }
        }

        private void ResetDeleteConfirmation()
        {
            pendingDeleteTaskId = null;
            pendingDeleteUntil = 0d;
        }

        private static string SafeValue(string value)
        {
            return string.IsNullOrEmpty(value) ? "-" : value;
        }

        private static Color GetStatusColor(string status)
        {
            switch (TaskBoardService.NormalizeStatus(status))
            {
                case "BackLog":
                    return new Color(0.94f, 0.94f, 0.94f);
                case "ToDo":
                    return new Color(0.85f, 0.96f, 0.85f);
                case "InProgress":
                    return new Color(0.84f, 0.91f, 1f);
                case "Review":
                    return new Color(1f, 0.9f, 0.82f);
                case "Done":
                    return new Color(0.88f, 0.88f, 0.88f);
                default:
                    return Color.white;
            }
        }

        private static Color GetPriorityColor(string priority)
        {
            switch (TaskBoardService.NormalizePriority(priority))
            {
                case "Lowest":
                    return new Color(0.42f, 0.52f, 0.61f);
                case "Low":
                    return new Color(0.45f, 0.45f, 0.45f);
                case "Medium":
                    return new Color(0.19f, 0.48f, 0.84f);
                case "High":
                    return new Color(0.88f, 0.54f, 0.16f);
                case "Highest":
                    return new Color(0.78f, 0.2f, 0.2f);
                default:
                    return new Color(0.35f, 0.35f, 0.35f);
            }
        }
    }
}
