using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using AxCopilot.Models;

namespace AxCopilot.Services;

public class ChatStorageService
{
	private static readonly string ConversationsDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "conversations");

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

	private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();

	private List<ChatConversation>? _metaCache;

	private bool _metaDirty = true;

	public ChatStorageService()
	{
		Directory.CreateDirectory(ConversationsDir);
	}

	public void Save(ChatConversation conversation)
	{
		conversation.UpdatedAt = DateTime.Now;
		if (string.IsNullOrEmpty(conversation.Preview) && conversation.Messages.Count > 0)
		{
			ChatMessage chatMessage = conversation.Messages.FirstOrDefault((ChatMessage m) => m.Role == "user");
			if (chatMessage != null)
			{
				conversation.Preview = ((chatMessage.Content.Length > 100) ? chatMessage.Content.Substring(0, 100) : chatMessage.Content);
			}
		}
		string content = JsonSerializer.Serialize(conversation, JsonOpts);
		string filePath = GetFilePath(conversation.Id);
		string text = filePath + ".tmp";
		Lock.EnterWriteLock();
		try
		{
			CryptoService.EncryptToFile(text, content);
			if (File.Exists(filePath))
			{
				File.Delete(filePath);
			}
			File.Move(text, filePath);
			UpdateMetaCache(conversation);
		}
		catch (Exception ex)
		{
			try
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
			catch
			{
			}
			LogService.Warn("대화 저장 실패 (" + conversation.Id + "): " + ex.Message);
			throw;
		}
		finally
		{
			Lock.ExitWriteLock();
		}
	}

	public ChatConversation? Load(string id)
	{
		string filePath = GetFilePath(id);
		Lock.EnterReadLock();
		try
		{
			if (!File.Exists(filePath))
			{
				return null;
			}
			string json = CryptoService.DecryptFromFile(filePath);
			return JsonSerializer.Deserialize<ChatConversation>(json, JsonOpts);
		}
		catch (Exception ex)
		{
			LogService.Warn("대화 로드 실패 (" + id + "): " + ex.Message);
			return null;
		}
		finally
		{
			Lock.ExitReadLock();
		}
	}

	public void InvalidateMetaCache()
	{
		_metaDirty = true;
	}

	public void UpdateMetaCache(ChatConversation conv)
	{
		if (_metaCache != null)
		{
			int num = _metaCache.FindIndex((ChatConversation c) => c.Id == conv.Id);
			ChatConversation chatConversation = new ChatConversation
			{
				Id = conv.Id,
				Title = conv.Title,
				CreatedAt = conv.CreatedAt,
				UpdatedAt = conv.UpdatedAt,
				Pinned = conv.Pinned,
				Category = conv.Category,
				Tab = conv.Tab,
				SystemCommand = conv.SystemCommand,
				WorkFolder = conv.WorkFolder,
				Preview = conv.Preview,
				Messages = new List<ChatMessage>(),
				ExecutionEvents = (conv.ExecutionEvents?.ToList() ?? new List<ChatExecutionEvent>())
			};
			if (num >= 0)
			{
				_metaCache[num] = chatConversation;
			}
			else
			{
				_metaCache.Add(chatConversation);
			}
		}
	}

	public void RemoveFromMetaCache(string id)
	{
		_metaCache?.RemoveAll((ChatConversation c) => c.Id == id);
	}

	public List<ChatConversation> LoadAllMeta()
	{
		if (!_metaDirty && _metaCache != null)
		{
			return (from c in _metaCache
				orderby c.Pinned descending, c.UpdatedAt descending
				select c).ToList();
		}
		List<ChatConversation> list = new List<ChatConversation>();
		if (!Directory.Exists(ConversationsDir))
		{
			_metaCache = list;
			_metaDirty = false;
			return list;
		}
		Lock.EnterReadLock();
		try
		{
			string[] files = Directory.GetFiles(ConversationsDir, "*.axchat");
			foreach (string text in files)
			{
				try
				{
					string json = CryptoService.DecryptFromFile(text);
					ChatConversation chatConversation = JsonSerializer.Deserialize<ChatConversation>(json, JsonOpts);
					if (chatConversation != null)
					{
						ChatConversation item = new ChatConversation
						{
							Id = chatConversation.Id,
							Title = chatConversation.Title,
							CreatedAt = chatConversation.CreatedAt,
							UpdatedAt = chatConversation.UpdatedAt,
							Pinned = chatConversation.Pinned,
							Category = chatConversation.Category,
							Tab = chatConversation.Tab,
							SystemCommand = chatConversation.SystemCommand,
							WorkFolder = chatConversation.WorkFolder,
							Preview = chatConversation.Preview,
							Messages = new List<ChatMessage>(),
							ExecutionEvents = (chatConversation.ExecutionEvents?.ToList() ?? new List<ChatExecutionEvent>())
						};
						list.Add(item);
					}
				}
				catch (Exception ex)
				{
					LogService.Warn("대화 메타 로드 실패 (" + Path.GetFileName(text) + "): " + ex.Message);
				}
			}
		}
		finally
		{
			Lock.ExitReadLock();
		}
		_metaCache = list;
		_metaDirty = false;
		return (from c in list
			orderby c.Pinned descending, c.UpdatedAt descending
			select c).ToList();
	}

	public void Delete(string id)
	{
		string filePath = GetFilePath(id);
		Lock.EnterWriteLock();
		try
		{
			if (File.Exists(filePath))
			{
				File.Delete(filePath);
			}
			RemoveFromMetaCache(id);
		}
		catch (Exception ex)
		{
			LogService.Warn("대화 삭제 실패 (" + id + "): " + ex.Message);
		}
		finally
		{
			Lock.ExitWriteLock();
		}
	}

	public int DeleteAll()
	{
		if (!Directory.Exists(ConversationsDir))
		{
			return 0;
		}
		Lock.EnterWriteLock();
		try
		{
			string[] files = Directory.GetFiles(ConversationsDir, "*.axchat");
			int num = 0;
			string[] array = files;
			foreach (string path in array)
			{
				try
				{
					File.Delete(path);
					num++;
				}
				catch (Exception ex)
				{
					LogService.Warn("파일 삭제 실패 (" + Path.GetFileName(path) + "): " + ex.Message);
				}
			}
			InvalidateMetaCache();
			return num;
		}
		finally
		{
			Lock.ExitWriteLock();
		}
	}

	public int PurgeExpired(int retentionDays)
	{
		if (retentionDays <= 0)
		{
			return 0;
		}
		DateTime dateTime = DateTime.Now.AddDays(-retentionDays);
		int num = 0;
		Lock.EnterWriteLock();
		try
		{
			string[] files = Directory.GetFiles(ConversationsDir, "*.axchat");
			foreach (string text in files)
			{
				try
				{
					string json = CryptoService.DecryptFromFile(text);
					ChatConversation chatConversation = JsonSerializer.Deserialize<ChatConversation>(json, JsonOpts);
					if (chatConversation != null && !chatConversation.Pinned && chatConversation.UpdatedAt < dateTime)
					{
						File.Delete(text);
						RemoveFromMetaCache(chatConversation.Id);
						num++;
					}
				}
				catch (Exception ex)
				{
					LogService.Warn("만료 대화 정리 실패 (" + Path.GetFileName(text) + "): " + ex.Message);
				}
			}
		}
		finally
		{
			Lock.ExitWriteLock();
		}
		return num;
	}

	public int PurgeForDiskSpace(double threshold = 0.98)
	{
		try
		{
			DriveInfo driveInfo = new DriveInfo(Path.GetPathRoot(ConversationsDir) ?? "C");
			if (driveInfo.TotalSize <= 0)
			{
				return 0;
			}
			double num = 1.0 - (double)driveInfo.AvailableFreeSpace / (double)driveInfo.TotalSize;
			if (num < threshold)
			{
				return 0;
			}
			LogService.Info($"드라이브 사용률 {num:P1} — 대화 정리 시작 (임계값: {threshold:P0})");
			var list = (from f in Directory.GetFiles(ConversationsDir, "*.axchat")
				select new
				{
					Path = f,
					LastWrite = File.GetLastWriteTime(f)
				} into f
				orderby f.LastWrite
				select f).ToList();
			int num2 = 0;
			Lock.EnterWriteLock();
			try
			{
				foreach (var item in list)
				{
					double num3 = 1.0 - (double)driveInfo.AvailableFreeSpace / (double)driveInfo.TotalSize;
					if (num3 < threshold - 0.02)
					{
						break;
					}
					try
					{
						string json = CryptoService.DecryptFromFile(item.Path);
						ChatConversation chatConversation = JsonSerializer.Deserialize<ChatConversation>(json, JsonOpts);
						if (chatConversation == null || !chatConversation.Pinned)
						{
							File.Delete(item.Path);
							num2++;
						}
					}
					catch
					{
					}
				}
			}
			finally
			{
				Lock.ExitWriteLock();
			}
			if (num2 > 0)
			{
				LogService.Info($"드라이브 용량 부족으로 대화 {num2}개 삭제 완료");
			}
			return num2;
		}
		catch (Exception ex)
		{
			LogService.Warn("디스크 용량 확인 실패: " + ex.Message);
			return 0;
		}
	}

	private static string GetFilePath(string id)
	{
		return Path.Combine(ConversationsDir, id + ".axchat");
	}
}
