using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.UIElements;

namespace Streamingle.Editor
{
    public sealed partial class AICameraGeneratorWindow
    {
        private static readonly string[] GeneratorUxmlPaths =
        {
            "Packages/com.mingle.cw-ai/Editor/UI/AICameraGeneratorWindow.uxml",
            "Assets/Scripts/Editor/TimelineTools/UI/AICameraGeneratorWindow.uxml"
        };

        private static readonly string[] GeneratorUssPaths =
        {
            "Packages/com.mingle.cw-ai/Editor/UI/AICameraGeneratorWindow.uss",
            "Assets/Scripts/Editor/TimelineTools/UI/AICameraGeneratorWindow.uss"
        };

        private VisualElement _generatorUiRoot;
        private ScrollView _mainScroll;
        private VisualElement _readinessCard;
        private Label _readinessTitle;
        private Label _readinessDetail;
        private DropdownField _sourceDirectorDropdown;
        private HelpBox _sourceSummaryBox;
        private DropdownField _cutRhythmDropdown;
        private Toggle _freshCandidateToggle;
        private HelpBox _freshCandidateWarning;
        private Button _generateFullButton;
        private VisualElement _refinementCard;
        private VisualElement _selectedShotPickerRow;
        private HelpBox _selectedShotInfo;
        private DropdownField _selectedShotDropdown;
        private Foldout _shotOptionsFoldout;
        private DropdownField _shotSizeDropdown;
        private DropdownField _motionDropdown;
        private DropdownField _compositionDropdown;
        private Slider _motionIntensitySlider;
        private FloatField _distanceField;
        private Slider _bodyFollowSlider;
        private Button _resetShotOptionsButton;
        private Button _regenerateShotButton;
        private Button _removeResultButton;
        private VisualElement _finalCard;
        private Foldout _feedbackFoldout;
        private TextField _editorStyleIdField;
        private Button _recordCorrectionButton;
        private Button _saveFinalSceneButton;
        private Foldout _advancedFoldout;
        private VisualElement _advancedConfigurationHost;
        private IMGUIContainer _advancedConfigurationGui;
        private Button _extractDatasetButton;
        private Button _reapplyFullButton;
        private Button _reapplyShotButton;
        private Button _buildCacheButton;
        private Button _refreshDiagnosticsButton;
        private Button _clearLogButton;
        private TextField _logField;
        private VisualElement _statusFooter;
        private Label _statusMessage;
        private Label _generationStageLabel;
        private ProgressBar _generationProgress;
        private Label _generationTimingLabel;
        private HelpBox _appliedSeedBox;
        private Button _statusLogButton;
        private Button _cancelGenerationButton;
        private IVisualElementScheduledItem _generatorUiRefreshItem;
        private PlayableDirector[] _uiSourceDirectors =
            Array.Empty<PlayableDirector>();
        private PlayableDirector _uiRenderedSourceDirector;
        private string _uiShotSignature = string.Empty;
        private string _uiRenderedLog = string.Empty;
        private bool _uiSourceReady;
        private bool _uiLibraryReady;
        private string _uiLibraryIssue = string.Empty;
        private double _nextSourceValidationTime;
        private double _nextLibraryValidationTime;

        public void CreateGUI()
        {
            _generatorUiRefreshItem?.Pause();
            var root = rootVisualElement;
            root.Clear();

            try
            {
                var uxmlPath = ResolveUiAssetPathForTests(false);
                var visualTree = string.IsNullOrWhiteSpace(uxmlPath)
                    ? null
                    : AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath);
                if (visualTree == null)
                {
                    throw new FileNotFoundException(
                        "AI 카메라 생성 UXML을 찾을 수 없습니다.",
                        uxmlPath);
                }

                visualTree.CloneTree(root);
                var ussPath = ResolveUiAssetPathForTests(true);
                var styleSheet = string.IsNullOrWhiteSpace(ussPath)
                    ? null
                    : AssetDatabase.LoadAssetAtPath<StyleSheet>(ussPath);
                if (styleSheet != null && !root.styleSheets.Contains(styleSheet))
                {
                    root.styleSheets.Add(styleSheet);
                }

                CacheGeneratorUiElements();
                BuildAdvancedConfigurationUi();
                RegisterGeneratorUiCallbacks();
                RefreshGeneratorUi(true);

                if (styleSheet == null)
                {
                    _generatorUiRoot.Insert(
                        0,
                        new HelpBox(
                            "스타일 파일을 찾지 못해 기본 Unity 스타일로 표시합니다.",
                            HelpBoxMessageType.Warning));
                }

                _generatorUiRefreshItem = root.schedule
                    .Execute(() => RefreshGeneratorUi(false))
                    .Every(200);
            }
            catch (Exception exception)
            {
                BuildGeneratorUiFallback(exception);
            }
        }

        internal static string ResolveUiAssetPathForTests(bool styleSheet)
        {
            var candidates = styleSheet
                ? GeneratorUssPaths
                : GeneratorUxmlPaths;
            foreach (var candidate in candidates)
            {
                var asset = styleSheet
                    ? (UnityEngine.Object)AssetDatabase.LoadAssetAtPath<StyleSheet>(
                        candidate)
                    : AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(candidate);
                if (asset != null)
                {
                    return candidate;
                }
            }

            return string.Empty;
        }

        private void CacheGeneratorUiElements()
        {
            _generatorUiRoot = RequireUi<VisualElement>("camera-generator-root");
            _mainScroll = RequireUi<ScrollView>("main-scroll");
            _readinessCard = RequireUi<VisualElement>("readiness-card");
            _readinessTitle = RequireUi<Label>("readiness-title");
            _readinessDetail = RequireUi<Label>("readiness-detail");
            _sourceDirectorDropdown =
                RequireUi<DropdownField>("source-director-dropdown");
            _sourceSummaryBox = RequireUi<HelpBox>("source-summary-box");
            _cutRhythmDropdown =
                RequireUi<DropdownField>("cut-rhythm-dropdown");
            _freshCandidateToggle =
                RequireUi<Toggle>("fresh-candidate-toggle");
            _freshCandidateWarning =
                RequireUi<HelpBox>("fresh-candidate-warning");
            _generateFullButton = RequireUi<Button>("generate-full-button");
            _refinementCard = RequireUi<VisualElement>("refinement-card");
            _selectedShotPickerRow =
                RequireUi<VisualElement>("selected-shot-picker-row");
            _selectedShotInfo = RequireUi<HelpBox>("selected-shot-info");
            _selectedShotDropdown =
                RequireUi<DropdownField>("selected-shot-dropdown");
            _shotOptionsFoldout = RequireUi<Foldout>("shot-options-foldout");
            _shotSizeDropdown = RequireUi<DropdownField>("shot-size-dropdown");
            _motionDropdown = RequireUi<DropdownField>("motion-dropdown");
            _compositionDropdown =
                RequireUi<DropdownField>("composition-dropdown");
            _motionIntensitySlider =
                RequireUi<Slider>("motion-intensity-slider");
            _distanceField = RequireUi<FloatField>("distance-field");
            _bodyFollowSlider = RequireUi<Slider>("body-follow-slider");
            _resetShotOptionsButton =
                RequireUi<Button>("reset-shot-options-button");
            _regenerateShotButton =
                RequireUi<Button>("regenerate-shot-button");
            _removeResultButton = RequireUi<Button>("remove-result-button");
            _finalCard = RequireUi<VisualElement>("final-card");
            _feedbackFoldout = RequireUi<Foldout>("feedback-foldout");
            _editorStyleIdField = RequireUi<TextField>("editor-style-id-field");
            _recordCorrectionButton =
                RequireUi<Button>("record-correction-button");
            _saveFinalSceneButton = RequireUi<Button>("save-final-scene-button");
            _advancedFoldout = RequireUi<Foldout>("advanced-foldout");
            _advancedConfigurationHost =
                RequireUi<VisualElement>("advanced-configuration-host");
            _extractDatasetButton = RequireUi<Button>("extract-dataset-button");
            _reapplyFullButton = RequireUi<Button>("reapply-full-button");
            _reapplyShotButton = RequireUi<Button>("reapply-shot-button");
            _buildCacheButton = RequireUi<Button>("build-cache-button");
            _refreshDiagnosticsButton =
                RequireUi<Button>("refresh-diagnostics-button");
            _clearLogButton = RequireUi<Button>("clear-log-button");
            _logField = RequireUi<TextField>("log-field");
            _statusFooter = RequireUi<VisualElement>("status-footer");
            _statusMessage = RequireUi<Label>("status-message");
            _generationStageLabel =
                RequireUi<Label>("generation-stage-label");
            _generationProgress = RequireUi<ProgressBar>("generation-progress");
            _generationTimingLabel =
                RequireUi<Label>("generation-timing-label");
            _appliedSeedBox = RequireUi<HelpBox>("applied-seed-box");
            _statusLogButton = RequireUi<Button>("status-log-button");
            _cancelGenerationButton =
                RequireUi<Button>("cancel-generation-button");
        }

        private T RequireUi<T>(string name) where T : VisualElement
        {
            var element = rootVisualElement.Q<T>(name);
            if (element == null)
            {
                throw new InvalidDataException(
                    $"AICameraGeneratorWindow.uxml에 '{name}' 요소가 없습니다.");
            }

            return element;
        }

        private void BuildAdvancedConfigurationUi()
        {
            _advancedConfigurationHost.Clear();
            _advancedConfigurationGui = new IMGUIContainer(
                DrawAdvancedConfigurationForUi);
            _advancedConfigurationGui.AddToClassList("advanced-imgui");
            _advancedConfigurationHost.Add(_advancedConfigurationGui);

            _logField.multiline = true;
            _logField.isReadOnly = true;
        }

        private void RegisterGeneratorUiCallbacks()
        {
            _sourceDirectorDropdown.RegisterValueChangedCallback(evt =>
            {
                var selectedIndex =
                    _sourceDirectorDropdown.choices.IndexOf(evt.newValue) - 1;
                _sourceDirector = selectedIndex >= 0 &&
                                  selectedIndex < _uiSourceDirectors.Length
                    ? _uiSourceDirectors[selectedIndex]
                    : null;
                RefreshShotNames();
                RefreshGeneratorUi(true);
            });

            _cutRhythmDropdown.RegisterValueChangedCallback(evt =>
            {
                var selectedIndex =
                    _cutRhythmDropdown.choices.IndexOf(evt.newValue);
                if (selectedIndex >= 0)
                {
                    _cutRhythm = (CutRhythmPreference)selectedIndex;
                }
            });

            _freshCandidateToggle.RegisterValueChangedCallback(evt =>
            {
                _forceFreshCandidatePool = evt.newValue;
                RefreshGeneratorUi(false);
            });

            _generateFullButton.clicked += () =>
            {
                ExportGenerateAndImport();
                RefreshGeneratorUi(true);
            };

            _selectedShotDropdown.RegisterValueChangedCallback(evt =>
            {
                var selectedIndex =
                    _selectedShotDropdown.choices.IndexOf(evt.newValue);
                if (selectedIndex >= 0 && selectedIndex < _shotNames.Length)
                {
                    _selectedShotIndex = selectedIndex;
                }
            });

            _shotOptionsFoldout.RegisterValueChangedCallback(evt =>
                _showDirectionSettings = evt.newValue);
            _shotSizeDropdown.RegisterValueChangedCallback(evt =>
            {
                var selectedIndex = _shotSizeDropdown.choices.IndexOf(evt.newValue);
                if (selectedIndex >= 0)
                {
                    _shotSize = (ShotSizePreference)selectedIndex;
                }
            });
            _motionDropdown.RegisterValueChangedCallback(evt =>
            {
                var selectedIndex = _motionDropdown.choices.IndexOf(evt.newValue);
                if (selectedIndex >= 0)
                {
                    _motion = (MotionPreference)selectedIndex;
                }
            });
            _compositionDropdown.RegisterValueChangedCallback(evt =>
            {
                var selectedIndex =
                    _compositionDropdown.choices.IndexOf(evt.newValue);
                if (selectedIndex >= 0)
                {
                    _composition = (CompositionPreference)selectedIndex;
                }
            });
            _motionIntensitySlider.RegisterValueChangedCallback(evt =>
                _motionIntensity = evt.newValue);
            _distanceField.RegisterValueChangedCallback(evt =>
                _distanceMeters = Mathf.Max(0f, evt.newValue));
            _bodyFollowSlider.RegisterValueChangedCallback(evt =>
                _bodyFollowStrength = evt.newValue);

            _resetShotOptionsButton.clicked += ResetSelectedShotOptions;
            _regenerateShotButton.clicked += () =>
            {
                StartGeneration(ProcessCompletionAction.ReplaceSelectedShot);
                RefreshGeneratorUi(false);
            };
            _removeResultButton.clicked += () =>
            {
                RunEditorAction(
                    "AI 카메라 결과를 제거하는 중...",
                    delegate
                    {
                        var result = RemoveScopedPreview();
                        RefreshShotNames();
                        return result;
                    });
                RefreshGeneratorUi(true);
            };

            _editorStyleIdField.RegisterValueChangedCallback(evt =>
                _editorStyleId = evt.newValue);
            _recordCorrectionButton.clicked += () =>
            {
                RunEditorAction(
                    "컷 수정 기록을 저장하는 중...",
                    CaptureCutCorrectionSnapshot);
                RefreshGeneratorUi(false);
            };
            _saveFinalSceneButton.clicked += SaveActiveSceneCopy;

            _feedbackFoldout.SetValueWithoutNotify(false);
            _advancedFoldout.SetValueWithoutNotify(_showAdvancedSettings);
            _advancedFoldout.RegisterValueChangedCallback(evt =>
            {
                _showAdvancedSettings = evt.newValue;
                if (evt.newValue)
                {
                    _advancedConfigurationGui?.MarkDirtyRepaint();
                    RefreshGeneratorUi(true);
                }
            });

            _extractDatasetButton.clicked += ExtractDatasetFromUi;
            _reapplyFullButton.clicked += () =>
            {
                ImportFullResult(_generatedFolder);
                RefreshGeneratorUi(true);
            };
            _reapplyShotButton.clicked += () =>
            {
                ImportSelectedShot(_generatedFolder, _selectedShotIndex);
                RefreshGeneratorUi(true);
            };
            _buildCacheButton.clicked += () =>
            {
                StartPreparationCacheBuild();
                RefreshGeneratorUi(false);
            };
            _refreshDiagnosticsButton.clicked += () =>
            {
                RefreshDatasetSongs(true);
                ValidateOrAutoSelectSourceDirector();
                RefreshShotNames();
                RefreshGeneratorUi(true);
            };
            _clearLogButton.clicked += () =>
            {
                _log = string.Empty;
                _uiRenderedLog = string.Empty;
                _logField.SetValueWithoutNotify("아직 로그가 없습니다.");
            };
            _statusLogButton.clicked += () =>
            {
                _showAdvancedSettings = true;
                _advancedFoldout.SetValueWithoutNotify(true);
                RefreshGeneratorUi(true);
                _mainScroll.schedule.Execute(() =>
                    _mainScroll.ScrollTo(_advancedFoldout));
            };
            _cancelGenerationButton.clicked += CancelGeneration;
            _generatorUiRoot.RegisterCallback<GeometryChangedEvent>(evt =>
                SetClassState(
                    _generatorUiRoot,
                    "narrow-layout",
                    evt.newRect.width > 0f && evt.newRect.width < 680f));
        }

        private void ResetSelectedShotOptions()
        {
            _shotSize = ShotSizePreference.Auto;
            _motion = MotionPreference.Auto;
            _composition = CompositionPreference.Auto;
            _motionIntensity = 1f;
            _distanceMeters = 0f;
            _bodyFollowStrength = 0.18f;
            RefreshGeneratorUi(false);
        }

        private void ExtractDatasetFromUi()
        {
            RunEditorAction(
                "Timeline 데이터를 추출하는 중...",
                delegate
                {
                    var output = TimelineCameraDatasetExporter
                        .ExportAll60FpsForCli();
                    ConfigureExportedDataset(output, _songId.Trim());
                    return $"추출 완료: {output}\n" +
                           $"곡 {_datasetSongNames.Length:N0}개를 찾았습니다.";
                });
            RefreshGeneratorUi(true);
        }

        private void DrawAdvancedConfigurationForUi()
        {
            EditorGUILayout.LabelField(
                "경로와 생성 세부 설정",
                EditorStyles.boldLabel);
            EditorGUILayout.HelpBox(
                "일반 생성에는 변경할 필요가 없습니다. Worker 경로, 입력 ID, " +
                "Seed와 키 단순화를 직접 조정할 때만 사용하세요.",
                MessageType.Info);
            using (new EditorGUI.DisabledScope(IsProcessRunning))
            {
                DrawPathSettings();
                EditorGUILayout.Space(10f);
                DrawTargetSettings();
            }
        }

        private void RefreshGeneratorUi(bool refreshLists)
        {
            if (_generatorUiRoot == null || _generatorUiRoot.panel == null)
            {
                return;
            }

            if (refreshLists || _uiRenderedSourceDirector != _sourceDirector)
            {
                RefreshSourceDirectorDropdown();
                _nextSourceValidationTime = 0d;
            }

            var shotSignature = string.Join("\u001f", _shotNames);
            if (refreshLists || !string.Equals(
                    shotSignature,
                    _uiShotSignature,
                    StringComparison.Ordinal))
            {
                RefreshShotDropdown();
                _uiShotSignature = shotSignature;
            }

            RefreshSourceSummary(refreshLists);
            RefreshGenerationLibraryStatus(refreshLists);
            RefreshReadiness();
            RefreshGenerationControls();
            RefreshRefinementControls();
            RefreshStatusFooter();
            RefreshAdvancedControls();
        }

        private void RefreshSourceDirectorDropdown()
        {
            _uiSourceDirectors = GetAvailableSourceDirectors();
            if (_sourceDirector != null &&
                !_uiSourceDirectors.Contains(_sourceDirector))
            {
                _sourceDirector = null;
            }

            if (_sourceDirector == null && _uiSourceDirectors.Length == 1)
            {
                _sourceDirector = _uiSourceDirectors[0];
                RefreshShotNames();
            }

            var choices = new List<string>();
            if (_uiSourceDirectors.Length == 0)
            {
                choices.Add("현재 씬에 사용할 Timeline이 없습니다");
            }
            else
            {
                choices.Add("Timeline을 선택하세요");
                choices.AddRange(_uiSourceDirectors.Select(GetSourceDirectorLabel));
            }

            _sourceDirectorDropdown.choices = choices;
            var currentIndex = Array.IndexOf(_uiSourceDirectors, _sourceDirector);
            var choiceIndex = _uiSourceDirectors.Length == 0
                ? 0
                : Mathf.Clamp(currentIndex + 1, 0, choices.Count - 1);
            _sourceDirectorDropdown.SetValueWithoutNotify(choices[choiceIndex]);
            _uiRenderedSourceDirector = _sourceDirector;
        }

        private void RefreshSourceSummary(bool force)
        {
            var now = EditorApplication.timeSinceStartup;
            if (!force && now < _nextSourceValidationTime)
            {
                return;
            }

            _nextSourceValidationTime = now + 1d;
            if (_sourceDirector == null)
            {
                _uiSourceReady = false;
                _sourceSummaryBox.text =
                    "모션과 음원이 들어 있는 원본 Timeline을 선택하세요.";
                _sourceSummaryBox.messageType = HelpBoxMessageType.Warning;
                return;
            }

            try
            {
                var summary = TimelineCameraDatasetExporter
                    .GetGenerationInputSummaryForEditor(_sourceDirector);
                _uiSourceReady = summary.isValid;
                if (!summary.isValid)
                {
                    _sourceSummaryBox.text = string.IsNullOrWhiteSpace(summary.error)
                        ? "캐릭터 모션과 음원을 확인할 수 없습니다."
                        : summary.error;
                    _sourceSummaryBox.messageType = HelpBoxMessageType.Error;
                    return;
                }

                _sourceSummaryBox.text =
                    $"캐릭터  {summary.characterName}\n" +
                    $"음원  {summary.audioClipName}    ·    " +
                    $"{summary.duration:F1}초";
                _sourceSummaryBox.messageType = HelpBoxMessageType.Info;
            }
            catch (Exception exception)
            {
                _uiSourceReady = false;
                _sourceSummaryBox.text = exception.Message;
                _sourceSummaryBox.messageType = HelpBoxMessageType.Error;
            }
        }

        private void RefreshReadiness()
        {
            var running = IsProcessRunning;
            var ready = _uiSourceReady && _uiLibraryReady;
            SetClassState(_readinessCard, "readiness--ready", !running && ready);
            SetClassState(_readinessCard, "readiness--running", running);
            SetClassState(
                _readinessCard,
                "readiness--warning",
                !running && !ready);

            if (running)
            {
                _readinessTitle.text = "카메라를 생성하고 있습니다";
                _readinessDetail.text =
                    "현재 단계와 예상 남은 시간은 아래 상태 영역에서 확인할 수 있습니다.";
            }
            else if (!_uiLibraryReady)
            {
                _readinessTitle.text = "생성 도구 설정이 필요합니다";
                _readinessDetail.text = _uiLibraryIssue;
            }
            else if (_uiSourceReady)
            {
                _readinessTitle.text = "Timeline 입력 확인 완료";
                _readinessDetail.text =
                    "모션과 음원이 확인되었습니다. 전체 생성 버튼을 눌러 시작하세요.";
            }
            else if (_sourceDirector == null)
            {
                _readinessTitle.text = "Timeline 선택이 필요합니다";
                _readinessDetail.text =
                    "현재 씬에서 모션과 음원이 있는 Timeline을 선택하세요.";
            }
            else
            {
                _readinessTitle.text = "입력 구성을 확인해주세요";
                _readinessDetail.text =
                    "선택한 Timeline에서 캐릭터 모션 또는 음원을 찾지 못했습니다.";
            }
        }

        private void RefreshGenerationLibraryStatus(bool force)
        {
            var now = EditorApplication.timeSinceStartup;
            if (!force && now < _nextLibraryValidationTime)
            {
                return;
            }

            _nextLibraryValidationTime = now + 1d;
            var repaired = TryAutoRepairGenerationLibraryRoot();
            var root = string.IsNullOrWhiteSpace(_cwAiRoot)
                ? string.Empty
                : Path.GetFullPath(_cwAiRoot);
            if (string.IsNullOrWhiteSpace(root) || !Directory.Exists(root))
            {
                _uiLibraryReady = false;
                _uiLibraryIssue =
                    "참조 데이터 폴더를 찾지 못했습니다. 고급·진단에서 경로를 확인하세요.";
                return;
            }

            if (!Directory.Exists(Path.Combine(root, "DatasetExports")) ||
                !File.Exists(Path.Combine(
                    root,
                    "reports",
                    "training_index.json")) ||
                !File.Exists(Path.Combine(
                    root,
                    "models",
                    "cut_ranker_v2.json")))
            {
                _uiLibraryReady = false;
                _uiLibraryIssue =
                    "참조 데이터가 완전하지 않습니다. 고급·진단에서 CW-AI 루트를 확인하세요.";
                return;
            }

            if (string.IsNullOrWhiteSpace(_cliExecutable) ||
                !File.Exists(_cliExecutable))
            {
                _cliExecutable = ResolveCliExecutable(true);
            }

            if (string.IsNullOrWhiteSpace(_cliExecutable) ||
                !File.Exists(_cliExecutable))
            {
                _uiLibraryReady = false;
                _uiLibraryIssue =
                    "CWCameraWorker를 찾지 못했습니다. 고급·진단에서 실행 파일을 확인하세요.";
                return;
            }

            _uiLibraryReady = true;
            _uiLibraryIssue = string.Empty;
            if (repaired &&
                !string.IsNullOrWhiteSpace(_status) &&
                _status.IndexOf(
                    "training_index",
                    StringComparison.OrdinalIgnoreCase) >= 0)
            {
                _status =
                    "참조 데이터 경로를 자동 복구했습니다. 전체 생성을 다시 실행하세요.";
            }
        }

        private void RefreshGenerationControls()
        {
            var running = IsProcessRunning;
            _sourceDirectorDropdown.SetEnabled(
                !running && _uiSourceDirectors.Length > 0);
            _cutRhythmDropdown.choices =
                new List<string>(SimpleCutRhythmNames);
            _cutRhythmDropdown.SetValueWithoutNotify(
                SimpleCutRhythmNames[Mathf.Clamp(
                    (int)_cutRhythm,
                    0,
                    SimpleCutRhythmNames.Length - 1)]);
            _cutRhythmDropdown.SetEnabled(!running);
            _freshCandidateToggle.SetValueWithoutNotify(_forceFreshCandidatePool);
            _freshCandidateToggle.SetEnabled(!running);
            SetDisplayed(_freshCandidateWarning, _forceFreshCandidatePool);
            _generateFullButton.text = running
                ? "카메라 생성 중..."
                : FullAutonomousGenerationButtonLabel;
            _generateFullButton.SetEnabled(
                !running && _uiSourceReady && _uiLibraryReady);
        }

        private void RefreshShotDropdown()
        {
            var choices = _shotNames.Length > 0
                ? new List<string>(_shotNames)
                : new List<string> { "가져온 AI 카메라가 없습니다" };
            _selectedShotDropdown.choices = choices;
            _selectedShotIndex = _shotNames.Length == 0
                ? 0
                : Mathf.Clamp(_selectedShotIndex, 0, _shotNames.Length - 1);
            _selectedShotDropdown.SetValueWithoutNotify(
                choices[Mathf.Clamp(_selectedShotIndex, 0, choices.Count - 1)]);
        }

        private void RefreshRefinementControls()
        {
            var hasShots = _shotNames.Length > 0;
            var running = IsProcessRunning;
            _editorStyleIdField.SetValueWithoutNotify(_editorStyleId);
            _recordCorrectionButton.SetEnabled(
                hasShots &&
                !running &&
                !string.IsNullOrWhiteSpace(_editorStyleId) &&
                _sourceDirector != null);
            _saveFinalSceneButton.SetEnabled(hasShots && !running);
            SetDisplayed(_refinementCard, hasShots);
            SetDisplayed(_finalCard, hasShots);
            SetDisplayed(_feedbackFoldout, hasShots);
            if (!hasShots)
            {
                return;
            }

            var selectedFromTimeline =
                TrySyncSelectedShotFromEditor(out var selectedCameraName);
            _selectedShotIndex = Mathf.Clamp(
                _selectedShotIndex,
                0,
                _shotNames.Length - 1);
            _selectedShotDropdown.SetValueWithoutNotify(
                _shotNames[_selectedShotIndex]);
            SetDisplayed(_selectedShotPickerRow, !selectedFromTimeline);
            _selectedShotInfo.text = selectedFromTimeline
                ? $"Timeline/Hierarchy 선택  {selectedCameraName}  ·  " +
                  $"카메라 {_selectedShotIndex:D3}"
                : "Timeline의 AI 카메라를 선택하면 자동으로 연결됩니다. " +
                  "아래 목록에서 직접 선택할 수도 있습니다.";
            _selectedShotInfo.messageType = selectedFromTimeline
                ? HelpBoxMessageType.Info
                : HelpBoxMessageType.None;

            _shotOptionsFoldout.SetValueWithoutNotify(_showDirectionSettings);
            _shotOptionsFoldout.SetEnabled(!running);
            _shotSizeDropdown.choices = new List<string>(SimpleShotSizeNames);
            _shotSizeDropdown.SetValueWithoutNotify(
                SimpleShotSizeNames[Mathf.Clamp(
                    (int)_shotSize,
                    0,
                    SimpleShotSizeNames.Length - 1)]);
            _motionDropdown.choices = new List<string>(SimpleMotionNames);
            _motionDropdown.SetValueWithoutNotify(
                SimpleMotionNames[Mathf.Clamp(
                    (int)_motion,
                    0,
                    SimpleMotionNames.Length - 1)]);
            _compositionDropdown.choices =
                new List<string>(SimpleCompositionNames);
            _compositionDropdown.SetValueWithoutNotify(
                SimpleCompositionNames[Mathf.Clamp(
                    (int)_composition,
                    0,
                    SimpleCompositionNames.Length - 1)]);
            _motionIntensitySlider.SetValueWithoutNotify(_motionIntensity);
            _distanceField.SetValueWithoutNotify(_distanceMeters);
            _bodyFollowSlider.SetValueWithoutNotify(_bodyFollowStrength);
            _regenerateShotButton.text = selectedFromTimeline
                ? "선택한 카메라 재생성"
                : "목록의 카메라 재생성";
            _regenerateShotButton.tooltip = selectedFromTimeline
                ? SelectedCameraRegenerationButtonLabel
                : ListedCameraRegenerationButtonLabel;
            _regenerateShotButton.SetEnabled(!running);
            _removeResultButton.SetEnabled(!running);

        }

        private void RefreshStatusFooter()
        {
            var running = IsProcessRunning;
            var status = string.IsNullOrWhiteSpace(_status)
                ? "준비됨"
                : _status.Trim();
            _statusMessage.text = status;

            var isError = status.IndexOf(
                              "실패",
                              StringComparison.OrdinalIgnoreCase) >= 0 ||
                          status.IndexOf(
                              "오류",
                              StringComparison.OrdinalIgnoreCase) >= 0 ||
                          status.IndexOf(
                              "찾을 수 없",
                              StringComparison.OrdinalIgnoreCase) >= 0;
            SetClassState(_statusFooter, "status--running", running);
            SetClassState(_statusFooter, "status--error", !running && isError);
            SetClassState(_statusFooter, "status--ready", !running && !isError);
            SetDisplayed(_statusLogButton, !running && isError);

            if (_generationOperationActive)
            {
                var stageText = _generationStageIndex > 0
                    ? $"{_generationStageIndex}/{_generationStageCount}단계 · " +
                      _generationStage
                    : _generationStage;
                _generationStageLabel.text = stageText;
                SetDisplayed(_generationStageLabel, true);
                SetDisplayed(
                    _generationProgress,
                    _generationStageProgress >= 0f);
                if (_generationStageProgress >= 0f)
                {
                    _generationProgress.value = Mathf.Clamp01(
                        _generationStageProgress);
                    _generationProgress.title = stageText;
                }

                _generationTimingLabel.text = BuildRunningTimingText();
                SetDisplayed(_generationTimingLabel, true);
            }
            else
            {
                SetDisplayed(_generationStageLabel, false);
                SetDisplayed(_generationProgress, false);
                var recentDuration = _lastCompletedDurationSeconds > 0d
                    ? _lastCompletedDurationSeconds
                    : EditorPrefs.GetFloat(
                        RuntimeHistoryKey("LastSeconds"),
                        -1f);
                _generationTimingLabel.text = recentDuration > 0d
                    ? "최근 생성  " + FormatObservedDuration(recentDuration)
                    : string.Empty;
                SetDisplayed(
                    _generationTimingLabel,
                    !isError && recentDuration > 0d);
            }

            SetDisplayed(_cancelGenerationButton, running);
            _cancelGenerationButton.SetEnabled(
                running && _cliRunner?.CancellationRequested != true);

            SetDisplayed(_appliedSeedBox, _hasLastAppliedSeed);
            if (_hasLastAppliedSeed)
            {
                _appliedSeedBox.text = BuildAppliedSeedSummary(
                    _lastAppliedSeed,
                    _seed,
                    _lastAppliedSeedWasSelectedShot,
                    _lastAppliedSeedShotIndex);
            }
        }

        private void RefreshAdvancedControls()
        {
            _advancedFoldout.SetValueWithoutNotify(_showAdvancedSettings);
            _extractDatasetButton.SetEnabled(!IsProcessRunning);
            _reapplyFullButton.SetEnabled(!IsProcessRunning);
            _reapplyShotButton.SetEnabled(
                !IsProcessRunning && _shotNames.Length > 0);
            _buildCacheButton.SetEnabled(
                !IsProcessRunning && _uiLibraryReady);
            _refreshDiagnosticsButton.SetEnabled(!IsProcessRunning);

            if (_showAdvancedSettings)
            {
                _advancedConfigurationGui?.MarkDirtyRepaint();
                var logText = string.IsNullOrWhiteSpace(_log)
                    ? "아직 로그가 없습니다."
                    : _log;
                if (!string.Equals(
                        logText,
                        _uiRenderedLog,
                        StringComparison.Ordinal))
                {
                    _uiRenderedLog = logText;
                    _logField.SetValueWithoutNotify(logText);
                }
            }
        }

        private void BuildGeneratorUiFallback(Exception exception)
        {
            var root = rootVisualElement;
            root.Clear();
            var helpBox = new HelpBox(
                "새 UI를 불러오지 못해 호환 화면으로 전환했습니다.\n" +
                exception.Message,
                HelpBoxMessageType.Error);
            helpBox.style.marginLeft = 8f;
            helpBox.style.marginRight = 8f;
            helpBox.style.marginTop = 8f;
            root.Add(helpBox);
            var legacyContainer = new IMGUIContainer(DrawLegacyGui);
            legacyContainer.style.flexGrow = 1f;
            root.Add(legacyContainer);
            UnityEngine.Debug.LogException(exception);
        }

        private void OnSelectionChange()
        {
            RefreshGeneratorUi(false);
        }

        private void OnHierarchyChange()
        {
            if (_generatorUiRoot == null)
            {
                return;
            }

            ValidateOrAutoSelectSourceDirector();
            RefreshShotNames();
            RefreshGeneratorUi(true);
        }

        private static void SetDisplayed(VisualElement element, bool displayed)
        {
            if (element != null)
            {
                element.style.display = displayed
                    ? DisplayStyle.Flex
                    : DisplayStyle.None;
            }
        }

        private static void SetClassState(
            VisualElement element,
            string className,
            bool enabled)
        {
            if (enabled)
            {
                element.AddToClassList(className);
            }
            else
            {
                element.RemoveFromClassList(className);
            }
        }
    }
}
