using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Tornado3_2026Election.Domain;
using Tornado3_2026Election.ViewModels;
using Windows.Graphics;
using Windows.Storage.Pickers;
using WinRT.Interop;

namespace Tornado3_2026Election;

public sealed partial class MainWindow : Window
{
    private const uint ImageIcon = 1;
    private const uint LoadFromFile = 0x0010;
    private const uint SetWindowIconMessage = 0x0080;
    private const int LargeIcon = 1;
    private const int SmallIcon = 0;
    private const int SmallIconWidthMetric = 49;
    private const int SmallIconHeightMetric = 50;
    private const int LargeIconWidthMetric = 11;
    private const int LargeIconHeightMetric = 12;

    private bool _suppressBroadcastPhaseToggle;
    private bool _suppressOperationModeToggle;
    private bool _startupFlowShown;
    private bool _windowIconApplied;
    private nint _largeWindowIconHandle;
    private nint _smallWindowIconHandle;

    public MainWindow()
    {
        ViewModel = new MainViewModel();
        InitializeComponent();
        Activated += MainWindow_Activated;
        ApplyWindowIcon();
        CaptureCurrentWindowPlacement();
        HookWindowPlacementTracking();
        MainNavigationView.SelectedItem = MainNavigationView.MenuItems[0];
        MainNavigationView.Header = ViewModel.CurrentPageTitle;
        MainNavigationView.Loaded += MainNavigationView_Loaded;
        Closed += MainWindow_Closed;
    }

    public MainViewModel ViewModel { get; }

    private void ApplyWindowIcon()
    {
        if (_windowIconApplied)
        {
            return;
        }

        try
        {
            var iconPath = Path.Combine(AppContext.BaseDirectory, "Assets", "AppIcon.ico");
            if (!File.Exists(iconPath))
            {
                return;
            }

            var windowHandle = WindowNative.GetWindowHandle(this);
            if (windowHandle == IntPtr.Zero)
            {
                return;
            }

            GetAppWindow()?.SetIcon(iconPath);
            ApplyNativeWindowIcons(windowHandle, iconPath);
            _windowIconApplied = true;
        }
        catch
        {
            // Ignore icon application failures and keep the window usable.
        }
    }

    private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
    {
        ApplyWindowIcon();
    }

    private async void MainNavigationView_Loaded(object sender, RoutedEventArgs e)
    {
        if (_startupFlowShown)
        {
            return;
        }

        _startupFlowShown = true;

        if (MainNavigationView.XamlRoot is null)
        {
            return;
        }

        await ApplySavedWindowPlacementAsync();

        if (await ViewModel.HasRestorableStateAsync())
        {
            await ViewModel.RestoreStartupStateAsync();
            EnsureNavigationSelection();
            return;
        }

        await ShowStationSelectionDialogAsync();
        await ViewModel.SaveStateAsync();
    }

    private async Task ShowStationSelectionDialogAsync()
    {
        var stationComboBox = new ComboBox
        {
            DisplayMemberPath = nameof(StationFilterItemViewModel.Name),
            SelectedValuePath = nameof(StationFilterItemViewModel.Id),
            ItemsSource = ViewModel.Settings.Stations,
            SelectedValue = ViewModel.Settings.SelectedStationId,
            MinWidth = 240
        };

        var stationDialog = new ContentDialog
        {
            XamlRoot = MainNavigationView.XamlRoot,
            Title = "방송사 선택",
            PrimaryButtonText = "확인",
            DefaultButton = ContentDialogButton.Primary,
            Content = new StackPanel
            {
                Spacing = 10,
                Children =
                {
                    new TextBlock
                    {
                        Text = "앱 시작 시 사용할 방송사를 선택하세요."
                    },
                    stationComboBox
                }
            }
        };

        stationDialog.PrimaryButtonClick += (_, _) =>
        {
            if (stationComboBox.SelectedValue is string stationId && !string.IsNullOrWhiteSpace(stationId))
            {
                ViewModel.Settings.SelectedStationId = stationId;
                MainNavigationView.Header = ViewModel.CurrentPageTitle;
            }
        };

        await ShowDialogAsync(stationDialog);
    }

    private async void PickImageRootFolderButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var windowHandle = WindowNative.GetWindowHandle(this);
            if (windowHandle == IntPtr.Zero)
            {
                return;
            }

            var picker = new FolderPicker
            {
                SuggestedStartLocation = PickerLocationId.ComputerFolder,
                CommitButtonText = "선택"
            };
            picker.FileTypeFilter.Add("*");
            InitializeWithWindow.Initialize(picker, windowHandle);

            var folder = await picker.PickSingleFolderAsync();
            if (folder is not null)
            {
                ViewModel.Settings.ImageRootPath = Services.TornadoPathResolver.GetDefaultT3CutPath();
            }
        }
        catch
        {
            // Ignore picker failures and keep the settings screen responsive.
        }
    }

    private async void BroadcastPhaseToggleSwitch_Toggled(object sender, RoutedEventArgs e)
    {
        if (_suppressBroadcastPhaseToggle || sender is not ToggleSwitch toggleSwitch)
        {
            return;
        }

        var targetPhase = toggleSwitch.IsOn ? BroadcastPhase.Counting : BroadcastPhase.PreElection;
        if (ViewModel.Data.BroadcastPhase == targetPhase)
        {
            return;
        }

        var confirmed = await ConfirmBroadcastPhaseChangeAsync(targetPhase);
        if (confirmed)
        {
            ViewModel.ApplyBroadcastPhase(targetPhase);
            EnsureNavigationSelection();
            return;
        }

        _suppressBroadcastPhaseToggle = true;
        toggleSwitch.IsOn = ViewModel.Data.IsCountingPhase;
        _suppressBroadcastPhaseToggle = false;
    }

    private async void OperationModeToggleSwitch_Toggled(object sender, RoutedEventArgs e)
    {
        if (_suppressOperationModeToggle || sender is not ToggleSwitch toggleSwitch)
        {
            return;
        }

        var targetMode = toggleSwitch.IsOn ? ChannelOperationMode.VideoWall : ChannelOperationMode.General;
        if (ViewModel.OperationMode == targetMode)
        {
            return;
        }

        var confirmed = await ConfirmOperationModeChangeAsync(targetMode);
        if (confirmed)
        {
            ViewModel.ApplyOperationMode(targetMode);
            EnsureNavigationSelection();
            return;
        }

        _suppressOperationModeToggle = true;
        toggleSwitch.IsOn = ViewModel.IsVideoWallOperationMode;
        _suppressOperationModeToggle = false;
    }

    private AppWindow? GetAppWindow()
    {
        try
        {
            var windowHandle = WindowNative.GetWindowHandle(this);
            if (windowHandle == IntPtr.Zero)
            {
                return null;
            }

            var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
            return AppWindow.GetFromWindowId(windowId);
        }
        catch
        {
            return null;
        }
    }

    private void HookWindowPlacementTracking()
    {
        var appWindow = GetAppWindow();
        if (appWindow is null)
        {
            return;
        }

        appWindow.Changed += (_, args) =>
        {
            if (args.DidPositionChange || args.DidSizeChange || args.DidPresenterChange)
            {
                CaptureCurrentWindowPlacement();
            }
        };
    }

    private async Task ApplySavedWindowPlacementAsync()
    {
        var savedWindowPlacement = await ViewModel.GetSavedWindowPlacementAsync();
        var appWindow = GetAppWindow();
        if (savedWindowPlacement is null || appWindow is null)
        {
            return;
        }

        var savedRect = new RectInt32(
            savedWindowPlacement.Value.X ?? appWindow.Position.X,
            savedWindowPlacement.Value.Y ?? appWindow.Position.Y,
            savedWindowPlacement.Value.Width,
            savedWindowPlacement.Value.Height);
        var displayArea = DisplayArea.GetFromRect(savedRect, DisplayAreaFallback.Nearest);
        var width = Math.Clamp(savedWindowPlacement.Value.Width, 640, displayArea.WorkArea.Width);
        var height = Math.Clamp(savedWindowPlacement.Value.Height, 480, displayArea.WorkArea.Height);
        var x = savedWindowPlacement.Value.X ?? appWindow.Position.X;
        var y = savedWindowPlacement.Value.Y ?? appWindow.Position.Y;
        var maxX = displayArea.WorkArea.X + Math.Max(0, displayArea.WorkArea.Width - width);
        var maxY = displayArea.WorkArea.Y + Math.Max(0, displayArea.WorkArea.Height - height);

        x = Math.Clamp(x, displayArea.WorkArea.X, maxX);
        y = Math.Clamp(y, displayArea.WorkArea.Y, maxY);

        appWindow.MoveAndResize(new RectInt32(x, y, width, height));
        ViewModel.UpdateWindowPlacement(x, y, width, height, savedWindowPlacement.Value.IsMaximized);

        if (savedWindowPlacement.Value.IsMaximized && appWindow.Presenter is OverlappedPresenter presenter)
        {
            presenter.Maximize();
        }
    }

    private void CaptureCurrentWindowPlacement()
    {
        var appWindow = GetAppWindow();
        if (appWindow is null)
        {
            return;
        }

        if (appWindow.Presenter is not OverlappedPresenter presenter)
        {
            ViewModel.UpdateWindowPlacement(appWindow.Position.X, appWindow.Position.Y, appWindow.Size.Width, appWindow.Size.Height, false);
            return;
        }

        if (presenter.State == OverlappedPresenterState.Minimized)
        {
            return;
        }

        var isMaximized = presenter.State == OverlappedPresenterState.Maximized;
        ViewModel.UpdateWindowPlacement(appWindow.Position.X, appWindow.Position.Y, appWindow.Size.Width, appWindow.Size.Height, isMaximized);
    }

    private static Task ShowDialogAsync(ContentDialog dialog)
    {
        var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

        void OnClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
        {
            sender.Closed -= OnClosed;
            completionSource.TrySetResult();
        }

        dialog.Closed += OnClosed;
        _ = dialog.ShowAsync();
        return completionSource.Task;
    }

    private async Task<bool> ConfirmBroadcastPhaseChangeAsync(BroadcastPhase targetPhase)
    {
        if (MainNavigationView.XamlRoot is null)
        {
            return false;
        }

        var targetLabel = targetPhase == BroadcastPhase.PreElection ? "투표" : "개표";
        var description = targetPhase == BroadcastPhase.PreElection
            ? "투표 단계에서는 투표율과 투표자 수 중심으로 수신합니다."
            : "개표 단계에서는 후보 득표수와 당선 판정 중심으로 수신합니다.";

        var dialog = new ContentDialog
        {
            XamlRoot = MainNavigationView.XamlRoot,
            Title = "방송 단계 변경",
            PrimaryButtonText = "변경",
            CloseButtonText = "취소",
            DefaultButton = ContentDialogButton.Close,
            Content = new StackPanel
            {
                Spacing = 10,
                Children =
                {
                    new TextBlock
                    {
                        Text = $"현재 방송 단계를 {targetLabel} 모드로 전환할까요?"
                    },
                    new TextBlock
                    {
                        Text = description,
                        TextWrapping = TextWrapping.WrapWholeWords
                    }
                }
            }
        };

        return await dialog.ShowAsync() == ContentDialogResult.Primary;
    }

    private async Task<bool> ConfirmOperationModeChangeAsync(ChannelOperationMode targetMode)
    {
        if (MainNavigationView.XamlRoot is null)
        {
            return false;
        }

        var targetLabel = targetMode == ChannelOperationMode.General ? "일반" : "VideoWall";
        var description = targetMode == ChannelOperationMode.General
            ? "일반 모드에서는 노멀, 좌상단, 하단만 보이고 VideoWall 메뉴와 패널은 숨깁니다."
            : "VideoWall 모드에서는 VideoWall만 보이고 노멀, 좌상단, 하단 메뉴와 패널은 숨깁니다.";

        var dialog = new ContentDialog
        {
            XamlRoot = MainNavigationView.XamlRoot,
            Title = "운영 모드 변경",
            PrimaryButtonText = "변경",
            CloseButtonText = "취소",
            DefaultButton = ContentDialogButton.Close,
            Content = new StackPanel
            {
                Spacing = 10,
                Children =
                {
                    new TextBlock
                    {
                        Text = $"현재 운영 모드를 {targetLabel} 모드로 전환할까요?"
                    },
                    new TextBlock
                    {
                        Text = description,
                        TextWrapping = TextWrapping.WrapWholeWords
                    }
                }
            }
        };

        return await dialog.ShowAsync() == ContentDialogResult.Primary;
    }

    private void MainNavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
    {
        if (args.SelectedItemContainer?.Tag is string tag)
        {
            ViewModel.Navigate(tag);
            MainNavigationView.Header = ViewModel.CurrentPageTitle;
        }
    }

    private void DistrictOverviewCard_Tapped(object sender, TappedRoutedEventArgs e)
    {
        if (sender is not FrameworkElement element ||
            element.DataContext is not DistrictOverviewCardViewModel card)
        {
            return;
        }

        ViewModel.Data.SelectDistrictOverviewCard(card);
    }

    private void EnsureNavigationSelection()
    {
        if (!ViewModel.IsPageAvailable(ViewModel.CurrentPage))
        {
            ViewModel.Navigate(ViewModel.IsVideoWallOperationMode ? "videowall" : "normal");
        }

        var targetTag = ViewModel.CurrentPage switch
        {
            AppPage.Normal => "normal",
            AppPage.TopLeft => "top-left",
            AppPage.Bottom => "bottom",
            AppPage.VideoWall => "videowall",
            AppPage.PreElectionData => "pre-election-data",
            AppPage.TurnoutData => "turnout-data",
            AppPage.CountingData => "counting-data",
            AppPage.Data => ViewModel.Data.IsPreElectionPhase ? "turnout-data" : "counting-data",
            AppPage.CareerPromiseData => "career-promises",
            AppPage.CutList => "cut-list",
            AppPage.Settings => "settings",
            AppPage.Log => "log",
            _ => ViewModel.IsVideoWallOperationMode ? "videowall" : "normal"
        };

        foreach (var item in MainNavigationView.MenuItems)
        {
            if (item is NavigationViewItem navigationItem && string.Equals(navigationItem.Tag as string, targetTag, StringComparison.Ordinal))
            {
                MainNavigationView.SelectedItem = navigationItem;
                break;
            }
        }

        MainNavigationView.Header = ViewModel.CurrentPageTitle;
    }

    private async void MainWindow_Closed(object sender, WindowEventArgs args)
    {
        CaptureCurrentWindowPlacement();
        ReleaseNativeWindowIcons();
        await ViewModel.ShutdownAsync();
    }

    private void ApplyNativeWindowIcons(IntPtr windowHandle, string iconPath)
    {
        _largeWindowIconHandle = LoadNativeWindowIcon(
            iconPath,
            GetSystemMetrics(LargeIconWidthMetric),
            GetSystemMetrics(LargeIconHeightMetric),
            _largeWindowIconHandle);

        if (_largeWindowIconHandle != IntPtr.Zero)
        {
            SendMessage(windowHandle, SetWindowIconMessage, (IntPtr)LargeIcon, _largeWindowIconHandle);
        }

        _smallWindowIconHandle = LoadNativeWindowIcon(
            iconPath,
            GetSystemMetrics(SmallIconWidthMetric),
            GetSystemMetrics(SmallIconHeightMetric),
            _smallWindowIconHandle);

        if (_smallWindowIconHandle != IntPtr.Zero)
        {
            SendMessage(windowHandle, SetWindowIconMessage, (IntPtr)SmallIcon, _smallWindowIconHandle);
        }
    }

    private void ReleaseNativeWindowIcons()
    {
        if (_largeWindowIconHandle != IntPtr.Zero)
        {
            DestroyIcon(_largeWindowIconHandle);
            _largeWindowIconHandle = IntPtr.Zero;
        }

        if (_smallWindowIconHandle != IntPtr.Zero)
        {
            DestroyIcon(_smallWindowIconHandle);
            _smallWindowIconHandle = IntPtr.Zero;
        }
    }

    private static nint LoadNativeWindowIcon(string iconPath, int width, int height, nint existingHandle)
    {
        if (existingHandle != IntPtr.Zero)
        {
            DestroyIcon(existingHandle);
        }

        return LoadImage(IntPtr.Zero, iconPath, ImageIcon, width, height, LoadFromFile);
    }

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern nint LoadImage(nint instanceHandle, string name, uint type, int width, int height, uint loadFlags);

    [DllImport("user32.dll")]
    private static extern nint SendMessage(IntPtr windowHandle, uint message, IntPtr wParam, nint lParam);

    [DllImport("user32.dll")]
    private static extern int GetSystemMetrics(int metric);

    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DestroyIcon(nint iconHandle);
}
