using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AxCopilot.Models;
using AxCopilot.Services;

namespace AxCopilot.Core;

public class ContextManager
{
	private struct RECT
	{
		public int Left;

		public int Top;

		public int Right;

		public int Bottom;
	}

	private struct WINDOWPLACEMENT
	{
		public uint length;

		public uint flags;

		public uint showCmd;

		public POINT ptMinPosition;

		public POINT ptMaxPosition;

		public RECT rcNormalPosition;
	}

	private struct POINT
	{
		public int x;

		public int y;
	}

	private delegate bool EnumWindowsProc(nint hWnd, nint lParam);

	private delegate bool MonitorEnumProc(nint hMonitor, nint hdcMonitor, ref RECT lprcMonitor, nint dwData);

	private readonly SettingsService _settings;

	private const uint SWP_NOZORDER = 4u;

	private const uint SWP_NOACTIVATE = 16u;

	private const uint MONITOR_DEFAULTTONEAREST = 2u;

	public ContextManager(SettingsService settings)
	{
		_settings = settings;
	}

	public WorkspaceProfile CaptureProfile(string name)
	{
		List<WindowSnapshot> snapshots = new List<WindowSnapshot>();
		Dictionary<nint, int> monitorMap = BuildMonitorMap();
		EnumWindows(delegate(nint hWnd, nint _)
		{
			if (!IsWindowVisible(hWnd))
			{
				return true;
			}
			if (IsIconic(hWnd))
			{
				return true;
			}
			string windowTitle = GetWindowTitle(hWnd);
			if (string.IsNullOrWhiteSpace(windowTitle))
			{
				return true;
			}
			if (IsSystemWindow(hWnd))
			{
				return true;
			}
			string processPath = GetProcessPath(hWnd);
			if (string.IsNullOrEmpty(processPath))
			{
				return true;
			}
			GetWindowRect(hWnd, out var lpRect);
			GetWindowPlacement(hWnd, out var lpwndpl);
			uint showCmd = lpwndpl.showCmd;
			if (1 == 0)
			{
			}
			string text = showCmd switch
			{
				1u => "Normal", 
				2u => "Minimized", 
				3u => "Maximized", 
				_ => "Normal", 
			};
			if (1 == 0)
			{
			}
			string showCmd2 = text;
			int monitorIndex = GetMonitorIndex(hWnd, monitorMap);
			snapshots.Add(new WindowSnapshot
			{
				Exe = processPath,
				Title = windowTitle,
				Rect = new WindowRect
				{
					X = lpRect.Left,
					Y = lpRect.Top,
					Width = lpRect.Right - lpRect.Left,
					Height = lpRect.Bottom - lpRect.Top
				},
				ShowCmd = showCmd2,
				Monitor = monitorIndex
			});
			return true;
		}, IntPtr.Zero);
		WorkspaceProfile workspaceProfile = new WorkspaceProfile
		{
			Name = name,
			Windows = snapshots,
			CreatedAt = DateTime.Now
		};
		WorkspaceProfile workspaceProfile2 = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
		if (workspaceProfile2 != null)
		{
			_settings.Settings.Profiles.Remove(workspaceProfile2);
		}
		_settings.Settings.Profiles.Add(workspaceProfile);
		_settings.Save();
		LogService.Info($"프로필 '{name}' 저장 완료: {snapshots.Count}개 창");
		return workspaceProfile;
	}

	public async Task<RestoreResult> RestoreProfileAsync(string name, CancellationToken ct = default(CancellationToken))
	{
		WorkspaceProfile profile = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
		if (profile == null)
		{
			return new RestoreResult(Success: false, "프로필 '" + name + "'을 찾을 수 없습니다.");
		}
		List<string> results = new List<string>();
		int monitorCount = GetMonitorCount();
		foreach (WindowSnapshot snapshot in profile.Windows)
		{
			ct.ThrowIfCancellationRequested();
			nint hWnd = FindMatchingWindow(snapshot);
			if (hWnd == IntPtr.Zero && File.Exists(snapshot.Exe))
			{
				try
				{
					Process.Start(new ProcessStartInfo(snapshot.Exe)
					{
						UseShellExecute = true
					});
					hWnd = await WaitForWindowAsync(snapshot.Exe, TimeSpan.FromSeconds(3.0), ct);
				}
				catch (Exception ex)
				{
					Exception ex2 = ex;
					results.Add($"⚠ {snapshot.Title}: 실행 실패 ({ex2.Message})");
					LogService.Warn("앱 실행 실패: " + snapshot.Exe + " - " + ex2.Message);
					continue;
				}
			}
			if (hWnd == IntPtr.Zero)
			{
				results.Add("⏭ " + snapshot.Title + ": 창 없음, 건너뜀");
				continue;
			}
			if (snapshot.Monitor >= monitorCount)
			{
				string policy = _settings.Settings.MonitorMismatch;
				if (policy == "skip")
				{
					results.Add("⏭ " + snapshot.Title + ": 모니터 불일치, 건너뜀");
					continue;
				}
			}
			try
			{
				nint hWnd2 = hWnd;
				string showCmd = snapshot.ShowCmd;
				if (1 == 0)
				{
				}
				string text = showCmd;
				int nCmdShow = ((text == "Maximized") ? 3 : ((!(text == "Minimized")) ? 9 : 2));
				if (1 == 0)
				{
				}
				ShowWindow(hWnd2, nCmdShow);
				if (snapshot.ShowCmd == "Normal")
				{
					SetWindowPos(hWnd, IntPtr.Zero, snapshot.Rect.X, snapshot.Rect.Y, snapshot.Rect.Width, snapshot.Rect.Height, 20u);
				}
				results.Add("✓ " + snapshot.Title + ": 복원 완료");
			}
			catch (Exception ex3)
			{
				results.Add($"⚠ {snapshot.Title}: 복원 실패 ({ex3.Message})");
				LogService.Warn("창 복원 실패 (권한 문제 가능): " + snapshot.Exe);
			}
		}
		LogService.Info("프로필 '" + name + "' 복원: " + string.Join(", ", results));
		return new RestoreResult(Success: true, string.Join("\n", results));
	}

	public bool DeleteProfile(string name)
	{
		WorkspaceProfile workspaceProfile = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
		if (workspaceProfile == null)
		{
			return false;
		}
		_settings.Settings.Profiles.Remove(workspaceProfile);
		_settings.Save();
		return true;
	}

	public bool RenameProfile(string oldName, string newName)
	{
		WorkspaceProfile workspaceProfile = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(oldName, StringComparison.OrdinalIgnoreCase));
		if (workspaceProfile == null)
		{
			return false;
		}
		workspaceProfile.Name = newName;
		_settings.Save();
		return true;
	}

	private static nint FindMatchingWindow(WindowSnapshot snapshot)
	{
		nint found = IntPtr.Zero;
		EnumWindows(delegate(nint hWnd, nint _)
		{
			string processPath = GetProcessPath(hWnd);
			if (string.Equals(processPath, snapshot.Exe, StringComparison.OrdinalIgnoreCase))
			{
				found = hWnd;
				return false;
			}
			return true;
		}, IntPtr.Zero);
		return found;
	}

	private static async Task<nint> WaitForWindowAsync(string exePath, TimeSpan timeout, CancellationToken ct)
	{
		DateTime deadline = DateTime.UtcNow + timeout;
		while (DateTime.UtcNow < deadline)
		{
			ct.ThrowIfCancellationRequested();
			nint hWnd = FindMatchingWindow(new WindowSnapshot
			{
				Exe = exePath
			});
			if (hWnd != IntPtr.Zero)
			{
				return hWnd;
			}
			await Task.Delay(200, ct);
		}
		return IntPtr.Zero;
	}

	private static string GetWindowTitle(nint hWnd)
	{
		StringBuilder stringBuilder = new StringBuilder(256);
		GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity);
		return stringBuilder.ToString();
	}

	private static string GetProcessPath(nint hWnd)
	{
		try
		{
			GetWindowThreadProcessId(hWnd, out var lpdwProcessId);
			if (lpdwProcessId == 0)
			{
				return "";
			}
			Process processById = Process.GetProcessById((int)lpdwProcessId);
			return processById.MainModule?.FileName ?? "";
		}
		catch
		{
			return "";
		}
	}

	private static bool IsSystemWindow(nint hWnd)
	{
		StringBuilder stringBuilder = new StringBuilder(256);
		GetClassName(hWnd, stringBuilder, stringBuilder.Capacity);
		switch (stringBuilder.ToString())
		{
		case "Shell_TrayWnd":
		case "Progman":
		case "WorkerW":
		case "DV2ControlHost":
			return true;
		default:
			return false;
		}
	}

	private static Dictionary<nint, int> BuildMonitorMap()
	{
		List<nint> monitors = new List<nint>();
		EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, delegate(nint hMonitor, nint hdcMonitor, ref RECT lprc, nint dwData)
		{
			monitors.Add(hMonitor);
			return true;
		}, IntPtr.Zero);
		return monitors.Select((nint hm, int idx) => (hm: hm, idx: idx)).ToDictionary(((nint hm, int idx) t) => t.hm, ((nint hm, int idx) t) => t.idx);
	}

	private static int GetMonitorIndex(nint hWnd, Dictionary<nint, int> map)
	{
		nint key = MonitorFromWindow(hWnd, 2u);
		int value;
		return map.TryGetValue(key, out value) ? value : 0;
	}

	private static int GetMonitorCount()
	{
		int count = 0;
		EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, delegate
		{
			count++;
			return true;
		}, IntPtr.Zero);
		return count;
	}

	[DllImport("user32.dll")]
	private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, nint lParam);

	[DllImport("user32.dll")]
	private static extern bool IsWindowVisible(nint hWnd);

	[DllImport("user32.dll")]
	private static extern bool IsIconic(nint hWnd);

	[DllImport("user32.dll")]
	private static extern int GetWindowText(nint hWnd, StringBuilder lpString, int nMaxCount);

	[DllImport("user32.dll")]
	private static extern bool GetWindowRect(nint hWnd, out RECT lpRect);

	[DllImport("user32.dll")]
	private static extern bool GetWindowPlacement(nint hWnd, out WINDOWPLACEMENT lpwndpl);

	[DllImport("user32.dll")]
	private static extern uint GetWindowThreadProcessId(nint hWnd, out uint lpdwProcessId);

	[DllImport("user32.dll")]
	private static extern int GetClassName(nint hWnd, StringBuilder lpClassName, int nMaxCount);

	[DllImport("user32.dll")]
	private static extern bool SetWindowPos(nint hWnd, nint hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

	[DllImport("user32.dll")]
	private static extern bool ShowWindow(nint hWnd, int nCmdShow);

	[DllImport("user32.dll")]
	private static extern nint MonitorFromWindow(nint hwnd, uint dwFlags);

	[DllImport("user32.dll")]
	private static extern bool EnumDisplayMonitors(nint hdc, nint lprcClip, MonitorEnumProc lpfnEnum, nint dwData);
}
