using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace AxCopilot.Services;

public class ClipboardHistoryService : IDisposable
{
	private class SavedClipEntry
	{
		public string Text { get; set; } = "";

		public DateTime CopiedAt { get; set; }

		public string? ImageBase64 { get; set; }

		public string? OriginalImagePath { get; set; }

		public bool IsPinned { get; set; }

		public string Category { get; set; } = "일반";
	}

	private const int WM_CLIPBOARDUPDATE = 797;

	private static readonly string HistoryPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "clipboard_history.dat");

	private static readonly string LegacyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "clipboard_history.json");

	private static readonly string ImageCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "clipboard_images");

	private const long MaxCacheSizeBytes = 524288000L;

	private const int MaxCacheAgeDays = 30;

	private static readonly JsonSerializerOptions JsonOpts = new JsonSerializerOptions
	{
		WriteIndented = false,
		PropertyNameCaseInsensitive = true
	};

	private readonly SettingsService _settings;

	private HwndSource? _msgSource;

	private readonly object _lock = new object();

	private volatile bool _ignoreNext;

	private bool _disposed;

	private readonly List<ClipboardEntry> _history = new List<ClipboardEntry>();

	public IReadOnlyList<ClipboardEntry> History
	{
		get
		{
			lock (_lock)
			{
				return _history.ToList();
			}
		}
	}

	public event EventHandler? HistoryChanged;

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

	public void Initialize()
	{
		if (!_disposed && _msgSource == null && _settings.Settings.ClipboardHistory.Enabled)
		{
			LoadHistory();
			((DispatcherObject)Application.Current).Dispatcher.Invoke((Action)delegate
			{
				HwndSourceParameters parameters = new HwndSourceParameters("axClipboardMonitor")
				{
					Width = 0,
					Height = 0,
					ParentWindow = new IntPtr(-3),
					WindowStyle = 0,
					ExtendedWindowStyle = 0
				};
				_msgSource = new HwndSource(parameters);
				_msgSource.AddHook(WndProc);
				AddClipboardFormatListener(_msgSource.Handle);
				LogService.Info("클립보드 히스토리 서비스 시작");
			});
		}
	}

	public void SuppressNextCapture()
	{
		_ignoreNext = true;
	}

	public void PromoteEntry(ClipboardEntry entry)
	{
		lock (_lock)
		{
			_history.Remove(entry);
			ClipboardEntry item = new ClipboardEntry(entry.Text, DateTime.Now)
			{
				Image = entry.Image,
				OriginalImagePath = entry.OriginalImagePath,
				IsPinned = entry.IsPinned,
				Category = entry.Category
			};
			_history.Insert(0, item);
		}
		this.HistoryChanged?.Invoke(this, EventArgs.Empty);
		SaveHistoryAsync();
	}

	public void ClearHistory()
	{
		lock (_lock)
		{
			_history.Clear();
		}
		this.HistoryChanged?.Invoke(this, EventArgs.Empty);
		SaveHistoryAsync();
	}

	public void TogglePin(ClipboardEntry entry)
	{
		lock (_lock)
		{
			if (!entry.IsPinned)
			{
				int maxPinnedClipboardItems = _settings.Settings.Launcher.MaxPinnedClipboardItems;
				int num = _history.Count((ClipboardEntry e) => e.IsPinned);
				if (num >= maxPinnedClipboardItems)
				{
					return;
				}
			}
			entry.IsPinned = !entry.IsPinned;
		}
		this.HistoryChanged?.Invoke(this, EventArgs.Empty);
		SaveHistoryAsync();
	}

	private static string DetectCategory(string text)
	{
		string text2 = text.Trim();
		if (Uri.TryCreate(text2, UriKind.Absolute, out Uri result) && (result.Scheme == "http" || result.Scheme == "https"))
		{
			return "URL";
		}
		if (text2.StartsWith("\\\\") || (text2.Length >= 3 && text2[1] == ':' && (text2[2] == '\\' || text2[2] == '/')))
		{
			return "경로";
		}
		if ((text2.Contains('{') && text2.Contains('}')) || text2.Contains("function ") || text2.Contains("class ") || text2.Contains("public ") || text2.Contains("private ") || text2.Contains("def ") || text2.Contains("import ") || text2.Contains("using "))
		{
			return "코드";
		}
		return "일반";
	}

	public void Reinitialize()
	{
		_disposed = false;
		_msgSource = null;
		Initialize();
		LogService.Info("클립보드 히스토리 서비스 재초기화 완료");
	}

	public void Dispose()
	{
		if (!_disposed)
		{
			_disposed = true;
			if (_msgSource != null)
			{
				RemoveClipboardFormatListener(_msgSource.Handle);
				_msgSource.Dispose();
				_msgSource = null;
			}
			SaveHistorySync();
		}
	}

	private void LoadHistory()
	{
		try
		{
			if (!File.Exists(HistoryPath) && File.Exists(LegacyPath))
			{
				MigrateLegacyFile();
			}
			if (!File.Exists(HistoryPath))
			{
				return;
			}
			byte[] encryptedData = File.ReadAllBytes(HistoryPath);
			byte[] bytes = ProtectedData.Unprotect(encryptedData, null, DataProtectionScope.CurrentUser);
			string json = Encoding.UTF8.GetString(bytes);
			List<SavedClipEntry> list = JsonSerializer.Deserialize<List<SavedClipEntry>>(json, JsonOpts);
			if (list == null)
			{
				return;
			}
			int maxItems = _settings.Settings.ClipboardHistory.MaxItems;
			lock (_lock)
			{
				foreach (SavedClipEntry item in list.Take(maxItems))
				{
					if (!string.IsNullOrEmpty(item.ImageBase64))
					{
						BitmapSource bitmapSource = Base64ToImage(item.ImageBase64);
						if (bitmapSource != null)
						{
							_history.Add(new ClipboardEntry("", item.CopiedAt)
							{
								Image = bitmapSource,
								OriginalImagePath = item.OriginalImagePath,
								IsPinned = item.IsPinned,
								Category = item.Category
							});
							continue;
						}
					}
					_history.Add(new ClipboardEntry(item.Text, item.CopiedAt)
					{
						IsPinned = item.IsPinned,
						Category = item.Category
					});
				}
			}
			Task.Run((Action)CleanupImageCache);
			LogService.Info($"클립보드 히스토리 {_history.Count}개 복원 (암호화)");
		}
		catch (Exception ex)
		{
			LogService.Warn("클립보드 히스토리 로드 실패: " + ex.Message);
		}
	}

	private void MigrateLegacyFile()
	{
		try
		{
			string s = File.ReadAllText(LegacyPath);
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			byte[] bytes2 = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
			Directory.CreateDirectory(Path.GetDirectoryName(HistoryPath));
			File.WriteAllBytes(HistoryPath, bytes2);
			File.Delete(LegacyPath);
			LogService.Info("클립보드 히스토리 평문→암호화 마이그레이션 완료");
		}
		catch (Exception ex)
		{
			LogService.Warn("클립보드 히스토리 마이그레이션 실패: " + ex.Message);
		}
	}

	private async Task SaveHistoryAsync()
	{
		try
		{
			List<SavedClipEntry> snapshot = BuildSnapshot();
			string json = JsonSerializer.Serialize(snapshot, JsonOpts);
			byte[] plain = Encoding.UTF8.GetBytes(json);
			byte[] encrypted = ProtectedData.Protect(plain, null, DataProtectionScope.CurrentUser);
			Directory.CreateDirectory(Path.GetDirectoryName(HistoryPath));
			await File.WriteAllBytesAsync(HistoryPath, encrypted).ConfigureAwait(continueOnCapturedContext: false);
		}
		catch (Exception ex)
		{
			Exception ex2 = ex;
			LogService.Warn("클립보드 히스토리 저장 실패: " + ex2.Message);
		}
	}

	private void SaveHistorySync()
	{
		try
		{
			List<SavedClipEntry> value = BuildSnapshot();
			string s = JsonSerializer.Serialize(value, JsonOpts);
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			byte[] bytes2 = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
			Directory.CreateDirectory(Path.GetDirectoryName(HistoryPath));
			File.WriteAllBytes(HistoryPath, bytes2);
		}
		catch
		{
		}
	}

	private List<SavedClipEntry> BuildSnapshot()
	{
		lock (_lock)
		{
			return _history.Select(delegate(ClipboardEntry e)
			{
				if (e.IsText)
				{
					return new SavedClipEntry
					{
						Text = e.Text,
						CopiedAt = e.CopiedAt,
						IsPinned = e.IsPinned,
						Category = e.Category
					};
				}
				string imageBase = ImageToBase64(e.Image);
				return new SavedClipEntry
				{
					CopiedAt = e.CopiedAt,
					ImageBase64 = imageBase,
					OriginalImagePath = e.OriginalImagePath,
					IsPinned = e.IsPinned,
					Category = e.Category
				};
			}).ToList();
		}
	}

	private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
	{
		if (msg == 797)
		{
			OnClipboardUpdate();
			handled = false;
		}
		return IntPtr.Zero;
	}

	private void OnClipboardUpdate()
	{
		if (_ignoreNext)
		{
			_ignoreNext = false;
		}
		else
		{
			if (!_settings.Settings.ClipboardHistory.Enabled)
			{
				return;
			}
			((DispatcherObject)Application.Current).Dispatcher.Invoke((Action)delegate
			{
				try
				{
					ClipboardEntry entry = null;
					if (Clipboard.ContainsText())
					{
						string text = Clipboard.GetText();
						if (string.IsNullOrWhiteSpace(text) || text.Length > 10000)
						{
							return;
						}
						foreach (string excludePattern in _settings.Settings.ClipboardHistory.ExcludePatterns)
						{
							try
							{
								if (Regex.IsMatch(text.Trim(), excludePattern, RegexOptions.None, TimeSpan.FromMilliseconds(200.0)))
								{
									return;
								}
							}
							catch
							{
							}
						}
						string category = (_settings.Settings.Launcher.EnableClipboardAutoCategory ? DetectCategory(text) : "일반");
						entry = new ClipboardEntry(text, DateTime.Now)
						{
							Category = category
						};
					}
					else if (Clipboard.ContainsImage())
					{
						BitmapSource image = Clipboard.GetImage();
						if (image == null)
						{
							return;
						}
						string originalImagePath = SaveOriginalImage(image);
						BitmapSource image2 = CreateThumbnail(image, 80);
						entry = new ClipboardEntry("", DateTime.Now)
						{
							Image = image2,
							OriginalImagePath = originalImagePath
						};
					}
					if (!(entry == null))
					{
						lock (_lock)
						{
							if (entry.IsText)
							{
								if (_history.Count > 0 && _history[0].Text == entry.Text)
								{
									return;
								}
								_history.RemoveAll((ClipboardEntry e) => e.IsText && e.Text == entry.Text);
							}
							_history.Insert(0, entry);
							int maxItems = _settings.Settings.ClipboardHistory.MaxItems;
							while (_history.Count > maxItems)
							{
								int num = _history.FindLastIndex((ClipboardEntry e) => !e.IsPinned);
								if (num < 0)
								{
									break;
								}
								_history.RemoveAt(num);
							}
						}
						this.HistoryChanged?.Invoke(this, EventArgs.Empty);
						SaveHistoryAsync();
					}
				}
				catch (Exception ex)
				{
					LogService.Warn("클립보드 캡처 실패: " + ex.Message);
				}
			});
		}
	}

	private static string? SaveOriginalImage(BitmapSource src)
	{
		try
		{
			Directory.CreateDirectory(ImageCachePath);
			string path = $"clip_{DateTime.Now:yyyyMMdd_HHmmss_fff}.png";
			string text = Path.Combine(ImageCachePath, path);
			PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
			pngBitmapEncoder.Frames.Add(BitmapFrame.Create(src));
			using FileStream stream = new FileStream(text, FileMode.Create);
			pngBitmapEncoder.Save(stream);
			return text;
		}
		catch (Exception ex)
		{
			LogService.Warn("원본 이미지 저장 실패: " + ex.Message);
			return null;
		}
	}

	public static BitmapSource? LoadOriginalImage(string? path)
	{
		if (string.IsNullOrEmpty(path) || !File.Exists(path))
		{
			return null;
		}
		try
		{
			BitmapImage bitmapImage = new BitmapImage();
			bitmapImage.BeginInit();
			bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
			bitmapImage.UriSource = new Uri(path, UriKind.Absolute);
			bitmapImage.EndInit();
			((Freezable)bitmapImage).Freeze();
			return bitmapImage;
		}
		catch
		{
			return null;
		}
	}

	public static void CleanupImageCache()
	{
		try
		{
			if (!Directory.Exists(ImageCachePath))
			{
				return;
			}
			List<FileInfo> list = (from f in new DirectoryInfo(ImageCachePath).GetFiles("clip_*.png")
				orderby f.LastWriteTime
				select f).ToList();
			DateTime cutoff = DateTime.Now.AddDays(-30.0);
			foreach (FileInfo item in list.Where((FileInfo f) => f.LastWriteTime < cutoff).ToList())
			{
				try
				{
					item.Delete();
					list.Remove(item);
				}
				catch
				{
				}
			}
			long num = list.Sum((FileInfo f) => f.Length);
			while (num > 524288000 && list.Count > 0)
			{
				FileInfo fileInfo = list[0];
				num -= fileInfo.Length;
				try
				{
					fileInfo.Delete();
				}
				catch
				{
				}
				list.RemoveAt(0);
			}
		}
		catch (Exception ex)
		{
			LogService.Warn("이미지 캐시 정리 실패: " + ex.Message);
		}
	}

	private static BitmapSource CreateThumbnail(BitmapSource src, int maxWidth)
	{
		if (src.PixelWidth <= maxWidth)
		{
			return src;
		}
		double num = (double)maxWidth / (double)src.PixelWidth;
		return new TransformedBitmap(src, new ScaleTransform(num, num));
	}

	private static string? ImageToBase64(BitmapSource? img)
	{
		if (img == null)
		{
			return null;
		}
		try
		{
			PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
			pngBitmapEncoder.Frames.Add(BitmapFrame.Create(img));
			using MemoryStream memoryStream = new MemoryStream();
			pngBitmapEncoder.Save(memoryStream);
			return Convert.ToBase64String(memoryStream.ToArray());
		}
		catch
		{
			return null;
		}
	}

	private static BitmapSource? Base64ToImage(string? base64)
	{
		if (string.IsNullOrEmpty(base64))
		{
			return null;
		}
		try
		{
			byte[] buffer = Convert.FromBase64String(base64);
			using MemoryStream bitmapStream = new MemoryStream(buffer);
			BitmapDecoder bitmapDecoder = BitmapDecoder.Create(bitmapStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
			return bitmapDecoder.Frames[0];
		}
		catch
		{
			return null;
		}
	}

	[DllImport("user32.dll", SetLastError = true)]
	private static extern bool AddClipboardFormatListener(nint hwnd);

	[DllImport("user32.dll", SetLastError = true)]
	private static extern bool RemoveClipboardFormatListener(nint hwnd);
}
