using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;

namespace AxCopilot.Views;

/// <summary>
/// CPU · RAM · 드라이브 · 상위 프로세스를 실시간으로 표시하는 플로팅 위젯.
/// info cpu 또는 info ram 항목의 Enter로 열립니다.
/// </summary>
public partial class ResourceMonitorWindow : Window
{
    // ─── P/Invoke ───────────────────────────────────────────────────────────

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

    [DllImport("user32.dll")] private static extern void ReleaseCapture();
    [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
    private const int WM_NCLBUTTONDOWN = 0xA1;
    private const int HTBOTTOMRIGHT    = 17;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct MEMORYSTATUSEX
    {
        public uint  dwLength;
        public uint  dwMemoryLoad;
        public ulong ullTotalPhys;
        public ulong ullAvailPhys;
        public ulong ullTotalPageFile;
        public ulong ullAvailPageFile;
        public ulong ullTotalVirtual;
        public ulong ullAvailVirtual;
        public ulong ullAvailExtendedVirtual;
    }

    // ─── 내부 모델 ──────────────────────────────────────────────────────────

    public sealed class DriveDisplayItem : INotifyPropertyChanged
    {
        private string _label    = "";
        private string _detail   = "";
        private double _barWidth = 0;
        private Brush  _barColor = Brushes.Green;

        public string Label    { get => _label;    set { _label = value;    OnPropertyChanged(); } }
        public string Detail   { get => _detail;   set { _detail = value;   OnPropertyChanged(); } }
        public double BarWidth { get => _barWidth; set { _barWidth = value; OnPropertyChanged(); } }
        public Brush  BarColor { get => _barColor; set { _barColor = value; OnPropertyChanged(); } }

        /// <summary>탐색기로 드라이브 루트를 여는 커맨드</summary>
        public ICommand OpenCommand { get; init; } = null!;

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string? n = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
    }

    public sealed class ProcessDisplayItem
    {
        public string Rank    { get; init; } = "";
        public string Name    { get; init; } = "";
        public string MemText { get; init; } = "";
        public double BarWidth{ get; init; }
    }

    // ─── 필드 ───────────────────────────────────────────────────────────────

    private readonly DispatcherTimer _timer;
    private static PerformanceCounter? _cpuCounter;
    private static float               _cpuCached;
    private static DateTime            _cpuUpdated = DateTime.MinValue;
    private readonly ObservableCollection<DriveDisplayItem> _drives = new();

    // 드라이브 진행바 최대 너비 (px)
    private const double MaxBarWidth = 160.0;

    public ResourceMonitorWindow()
    {
        InitializeComponent();
        DriveList.ItemsSource = _drives;

        // 첫 CPU 카운터 초기화 (첫 샘플은 0이라 무의미)
        if (_cpuCounter == null)
        {
            try
            {
                _cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
                _cpuCounter.NextValue();
            }
            catch (Exception) { /* PerformanceCounter 미지원 환경 */ }
        }

        // 드라이브 목록 초기 구성 (클릭 커맨드 포함)
        InitDrives();

        // 1초 주기 갱신
        _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
        _timer.Tick += (_, _) => Refresh();
        _timer.Start();

        Refresh(); // 즉시 첫 갱신
    }

    // ─── 드라이브 목록 초기화 ───────────────────────────────────────────────

    private void InitDrives()
    {
        _drives.Clear();
        foreach (var d in DriveInfo.GetDrives().Where(d => d.IsReady && d.DriveType == DriveType.Fixed))
        {
            var root = d.RootDirectory.FullName;
            _drives.Add(new DriveDisplayItem
            {
                OpenCommand = new RelayCommand(() =>
                {
                    try { Process.Start(new ProcessStartInfo("explorer.exe", root) { UseShellExecute = true }); }
                    catch (Exception) { /* 무시 */ }
                })
            });
        }
    }

    // ─── 갱신 ────────────────────────────────────────────────────────────────

    private void Refresh()
    {
        RefreshCpu();
        RefreshRam();
        RefreshDrives();
        RefreshProcesses();
        RefreshUptime();
    }

    private void RefreshCpu()
    {
        try
        {
            if (_cpuCounter == null) { CpuValueText.Text = "—"; return; }
            if ((DateTime.Now - _cpuUpdated).TotalMilliseconds > 800)
            {
                _cpuCached  = _cpuCounter.NextValue();
                _cpuUpdated = DateTime.Now;
            }
            var pct = (int)Math.Clamp(_cpuCached, 0, 100);
            CpuValueText.Text = $"{pct}%";

            // 색상: 낮음=파랑, 높음=빨강
            var barBrush = pct > 80 ? new SolidColorBrush(Color.FromRgb(0xDC, 0x26, 0x26))
                         : pct > 50 ? new SolidColorBrush(Color.FromRgb(0xD9, 0x77, 0x06))
                         : new SolidColorBrush(Color.FromRgb(0x4B, 0x9E, 0xFC));
            CpuValueText.Foreground = barBrush;
            CpuBar.Background       = barBrush;
            CpuBar.Width = (CpuBar.Parent as FrameworkElement)?.ActualWidth * pct / 100.0 ?? 0;

            // 프로세서 이름 (최초 1회)
            if (string.IsNullOrEmpty(CpuNameText.Text))
                CpuNameText.Text = GetProcessorName();
        }
        catch (Exception) { CpuValueText.Text = "—"; }
    }

    private void RefreshRam()
    {
        var mem = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf<MEMORYSTATUSEX>() };
        if (!GlobalMemoryStatusEx(ref mem)) return;

        var totalGb = mem.ullTotalPhys / 1024.0 / 1024 / 1024;
        var usedGb  = (mem.ullTotalPhys - mem.ullAvailPhys) / 1024.0 / 1024 / 1024;
        var freeGb  = mem.ullAvailPhys / 1024.0 / 1024 / 1024;
        var pct     = (int)mem.dwMemoryLoad;

        RamValueText.Text   = $"{pct}%";
        RamDetailText.Text  = $"사용: {usedGb:F1} GB  /  전체: {totalGb:F1} GB  /  여유: {freeGb:F1} GB";
        RamBar.Width = (RamBar.Parent as FrameworkElement)?.ActualWidth * pct / 100.0 ?? 0;

        var barBrush = pct > 85 ? new SolidColorBrush(Color.FromRgb(0xDC, 0x26, 0x26))
                     : pct > 65 ? new SolidColorBrush(Color.FromRgb(0xD9, 0x77, 0x06))
                     : new SolidColorBrush(Color.FromRgb(0x7C, 0x3A, 0xED));
        RamValueText.Foreground = barBrush;
        RamBar.Background       = barBrush;
    }

    private void RefreshDrives()
    {
        var drives = DriveInfo.GetDrives()
            .Where(d => d.IsReady && d.DriveType == DriveType.Fixed)
            .ToList();

        // 드라이브 수가 바뀌면 재초기화
        if (drives.Count != _drives.Count) InitDrives();

        for (int i = 0; i < drives.Count && i < _drives.Count; i++)
        {
            var d = drives[i];
            var totalGb = d.TotalSize / 1024.0 / 1024 / 1024;
            var freeGb  = d.AvailableFreeSpace / 1024.0 / 1024 / 1024;
            var usedGb  = totalGb - freeGb;
            var pct     = (int)(usedGb / totalGb * 100);
            var lbl     = string.IsNullOrWhiteSpace(d.VolumeLabel) ? d.Name.TrimEnd('\\') : $"{d.Name.TrimEnd('\\')} ({d.VolumeLabel})";

            _drives[i].Label    = lbl;
            _drives[i].Detail   = $"{freeGb:F1}GB 여유";
            _drives[i].BarWidth = MaxBarWidth * pct / 100.0;
            _drives[i].BarColor = pct > 90
                ? new SolidColorBrush(Color.FromRgb(0xDC, 0x26, 0x26))
                : pct > 75
                ? new SolidColorBrush(Color.FromRgb(0xD9, 0x77, 0x06))
                : new SolidColorBrush(Color.FromRgb(0x05, 0x96, 0x69));
        }
    }

    private void RefreshProcesses()
    {
        try
        {
            // 메모리 기준 상위 7개 (WorkingSet64 — 비동기 수집 없이도 빠름)
            var procs = Process.GetProcesses()
                .OrderByDescending(p => { try { return p.WorkingSet64; } catch (Exception) { return 0L; } })
                .Take(7)
                .ToList();

            long maxMem = procs.Count > 0 ? (long)(procs[0].WorkingSet64 > 0 ? procs[0].WorkingSet64 : 1) : 1;

            var items = procs.Select((p, i) =>
            {
                long ws  = 0;
                string name = "";
                try { ws = p.WorkingSet64; name = p.ProcessName; } catch (Exception) { name = "—"; }
                var mb = ws / 1024.0 / 1024;
                return new ProcessDisplayItem
                {
                    Rank     = $"{i + 1}",
                    Name     = name,
                    MemText  = mb > 1024 ? $"{mb / 1024:F1} GB" : $"{mb:F0} MB",
                    BarWidth = Math.Max(2, 46.0 * ws / maxMem)
                };
            }).ToList();

            ProcessList.ItemsSource = items;
        }
        catch (Exception) { /* 권한 부족 등 무시 */ }
    }

    private void RefreshUptime()
    {
        var uptime = TimeSpan.FromMilliseconds(Environment.TickCount64);
        var d = (int)uptime.TotalDays;
        var h = uptime.Hours;
        var m = uptime.Minutes;
        UptimeText.Text = d > 0 ? $"가동: {d}일 {h}시간 {m}분"
                        : h > 0 ? $"가동: {h}시간 {m}분"
                        : $"가동: {m}분 {uptime.Seconds}초";
    }

    // ─── 이벤트 ─────────────────────────────────────────────────────────────

    private void ResizeGrip_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (e.ButtonState != System.Windows.Input.MouseButtonState.Pressed) return;
        ReleaseCapture();
        SendMessage(new WindowInteropHelper(this).Handle, WM_NCLBUTTONDOWN, (IntPtr)HTBOTTOMRIGHT, IntPtr.Zero);
    }

    private void Close_Click(object sender, RoutedEventArgs e) => Close();

    private void Window_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Escape) Close();
    }

    private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Left && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
            try { DragMove(); } catch (Exception) { }
    }

    /// <summary>Ctrl+마우스 휠로 창 높이 조절 (투명 창에서는 OS 리사이즈 그립이 없으므로)</summary>
    protected override void OnMouseWheel(System.Windows.Input.MouseWheelEventArgs e)
    {
        if (Keyboard.Modifiers == ModifierKeys.Control)
        {
            var delta = e.Delta > 0 ? 40 : -40;
            var newH = Height + delta;
            if (newH >= MinHeight && newH <= 900)
                Height = newH;
            e.Handled = true;
            return;
        }
        base.OnMouseWheel(e);
    }

    protected override void OnClosed(EventArgs e)
    {
        _timer.Stop();
        base.OnClosed(e);
    }

    // ─── 헬퍼 ───────────────────────────────────────────────────────────────

    private static string GetProcessorName()
    {
        try
        {
            using var key = Microsoft.Win32.Registry.LocalMachine
                .OpenSubKey(@"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
            return key?.GetValue("ProcessorNameString") as string ?? "";
        }
        catch (Exception) { return ""; }
    }

    /// <summary>간단한 ICommand 구현 (RelayCommand)</summary>
    private sealed class RelayCommand(Action execute) : ICommand
    {
        public event EventHandler? CanExecuteChanged { add { } remove { } }
        public bool CanExecute(object? _) => true;
        public void Execute(object? _)    => execute();
    }
}
