using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Wordprocessing;
using UglyToad.PdfPig;
using UglyToad.PdfPig.Content;

namespace AxCopilot.Services.Agent;

public class DocumentReaderTool : IAgentTool
{
	private static readonly HashSet<string> TextExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
	{
		".txt", ".log", ".json", ".xml", ".md", ".csv", ".tsv", ".yaml", ".yml", ".ini",
		".cfg", ".conf", ".properties", ".html", ".htm", ".css", ".js", ".ts", ".py", ".cs",
		".java", ".sql", ".sh", ".bat", ".ps1", ".r", ".m"
	};

	private const int DefaultMaxChars = 8000;

	public string Name => "document_read";

	public string Description => "Read a document file and extract its text content. Supports: PDF (.pdf), Word (.docx), Excel (.xlsx), CSV (.csv), text (.txt/.log/.json/.xml/.md), BibTeX (.bib), RIS (.ris). For large files, use 'offset' to read from a specific character position (chunked reading). For large PDFs, use 'pages' parameter to read specific page ranges (e.g., '1-5', '10-20'). Use 'section' parameter with value 'references' to extract only the references/bibliography section from a PDF.";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema obj = new ToolParameterSchema
			{
				Properties = new Dictionary<string, ToolProperty>
				{
					["path"] = new ToolProperty
					{
						Type = "string",
						Description = "Document file path (absolute or relative to work folder)"
					},
					["max_chars"] = new ToolProperty
					{
						Type = "integer",
						Description = "Maximum characters to extract per chunk. Default: 8000. Use smaller values for summaries."
					},
					["offset"] = new ToolProperty
					{
						Type = "integer",
						Description = "Character offset to start reading from. Default: 0. Use this to read the next chunk of a large file (value from 'next_offset' in previous response)."
					},
					["sheet"] = new ToolProperty
					{
						Type = "string",
						Description = "For Excel files: sheet name or 1-based index. Default: first sheet."
					},
					["pages"] = new ToolProperty
					{
						Type = "string",
						Description = "For PDF files: page range to read (e.g., '1-5', '3', '10-20'). Default: all pages."
					},
					["section"] = new ToolProperty
					{
						Type = "string",
						Description = "Extract specific section. 'references' = extract references/bibliography from PDF. 'abstract' = extract abstract."
					}
				}
			};
			int num = 1;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			CollectionsMarshal.AsSpan(list)[0] = "path";
			obj.Required = list;
			return obj;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
	{
		if (!args.TryGetProperty("path", out var pathEl))
		{
			return ToolResult.Fail("path가 필요합니다.");
		}
		string path = pathEl.GetString() ?? "";
		JsonElement mc;
		int maxChars = (args.TryGetProperty("max_chars", out mc) ? GetIntValue(mc, 8000) : 8000);
		JsonElement off;
		int offset = (args.TryGetProperty("offset", out off) ? GetIntValue(off, 0) : 0);
		JsonElement sh;
		string sheetParam = (args.TryGetProperty("sheet", out sh) ? (sh.GetString() ?? "") : "");
		JsonElement pg;
		string pagesParam = (args.TryGetProperty("pages", out pg) ? (pg.GetString() ?? "") : "");
		JsonElement sec;
		string sectionParam = (args.TryGetProperty("section", out sec) ? (sec.GetString() ?? "") : "");
		if (maxChars < 100)
		{
			maxChars = 8000;
		}
		if (offset < 0)
		{
			offset = 0;
		}
		string fullPath = FileReadTool.ResolvePath(path, context.WorkFolder);
		if (!context.IsPathAllowed(fullPath))
		{
			return ToolResult.Fail("경로 접근 차단: " + fullPath);
		}
		if (!File.Exists(fullPath))
		{
			return ToolResult.Fail("파일이 존재하지 않습니다: " + fullPath);
		}
		string ext = Path.GetExtension(fullPath).ToLowerInvariant();
		try
		{
			int extractMax = ((offset > 0) ? (offset + maxChars + 100) : maxChars);
			string text = ext;
			if (1 == 0)
			{
			}
			string text2 = text switch
			{
				".pdf" => await Task.Run(() => ReadPdf(fullPath, extractMax, pagesParam, sectionParam), ct), 
				".docx" => await Task.Run(() => ReadDocx(fullPath, extractMax), ct), 
				".xlsx" => await Task.Run(() => ReadXlsx(fullPath, sheetParam, extractMax), ct), 
				".bib" => await Task.Run(() => ReadBibTeX(fullPath, extractMax), ct), 
				".ris" => await Task.Run(() => ReadRis(fullPath, extractMax), ct), 
				_ => (!TextExtensions.Contains(ext)) ? null : (await ReadTextFile(fullPath, extractMax, ct)), 
			};
			if (1 == 0)
			{
			}
			string text3 = text2;
			if (text3 == null)
			{
				return ToolResult.Fail("지원하지 않는 파일 형식: " + ext);
			}
			int totalExtracted = text3.Length;
			if (offset > 0)
			{
				if (offset >= text3.Length)
				{
					return ToolResult.Ok($"[{Path.GetFileName(fullPath)}] offset {offset}은 문서 끝을 초과합니다 (전체 {text3.Length}자).", fullPath);
				}
				text2 = text3;
				int num = offset;
				text3 = text2.Substring(num, text2.Length - num);
			}
			bool hasMore = text3.Length > maxChars;
			if (hasMore)
			{
				text3 = text3.Substring(0, maxChars);
			}
			FileInfo fileInfo = new FileInfo(fullPath);
			string header = $"[{Path.GetFileName(fullPath)}] ({ext.TrimStart('.')}, {FormatSize(fileInfo.Length)})";
			if (offset > 0)
			{
				header += $" — offset {offset}부터 {maxChars}자 읽음";
			}
			if (hasMore)
			{
				int nextOffset = offset + maxChars;
				header += $"\n⚡ 추가 내용이 있습니다. 다음 청크를 읽으려면 offset={nextOffset}을 사용하세요.";
			}
			else if (offset > 0)
			{
				header += " — 문서 끝까지 읽음 ✓";
			}
			else if (totalExtracted >= maxChars)
			{
				header += $" — 처음 {maxChars}자만 추출됨. 계속 읽으려면 offset={maxChars}을 사용하거나, pages 파라미터로 특정 페이지를 지정하세요.";
			}
			return ToolResult.Ok(header + "\n\n" + text3, fullPath);
		}
		catch (Exception ex)
		{
			return ToolResult.Fail("문서 읽기 실패: " + ex.Message);
		}
	}

	private static string ReadPdf(string path, int maxChars, string pagesParam, string sectionParam)
	{
		StringBuilder stringBuilder = new StringBuilder();
		using PdfDocument pdfDocument = PdfDocument.Open(path);
		int numberOfPages = pdfDocument.NumberOfPages;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder3 = stringBuilder2;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
		handler.AppendLiteral("PDF: ");
		handler.AppendFormatted(numberOfPages);
		handler.AppendLiteral("페이지");
		stringBuilder3.AppendLine(ref handler);
		stringBuilder.AppendLine();
		var (num, num2) = ParsePageRange(pagesParam, numberOfPages);
		if (string.Equals(sectionParam, "references", StringComparison.OrdinalIgnoreCase))
		{
			return ExtractReferences(pdfDocument, numberOfPages, maxChars);
		}
		if (string.Equals(sectionParam, "abstract", StringComparison.OrdinalIgnoreCase))
		{
			return ExtractAbstract(pdfDocument, numberOfPages, maxChars);
		}
		stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder4 = stringBuilder2;
		handler = new StringBuilder.AppendInterpolatedStringHandler(15, 3, stringBuilder2);
		handler.AppendLiteral("읽는 범위: ");
		handler.AppendFormatted(num);
		handler.AppendLiteral("-");
		handler.AppendFormatted(num2);
		handler.AppendLiteral(" / ");
		handler.AppendFormatted(numberOfPages);
		handler.AppendLiteral(" 페이지");
		stringBuilder4.AppendLine(ref handler);
		stringBuilder.AppendLine();
		for (int i = num; i <= num2; i++)
		{
			if (stringBuilder.Length >= maxChars)
			{
				break;
			}
			UglyToad.PdfPig.Content.Page page = pdfDocument.GetPage(i);
			string text = page.Text;
			if (!string.IsNullOrWhiteSpace(text))
			{
				stringBuilder2 = stringBuilder;
				StringBuilder stringBuilder5 = stringBuilder2;
				handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder2);
				handler.AppendLiteral("--- Page ");
				handler.AppendFormatted(i);
				handler.AppendLiteral(" ---");
				stringBuilder5.AppendLine(ref handler);
				stringBuilder.AppendLine(text.Trim());
				stringBuilder.AppendLine();
			}
		}
		return Truncate(stringBuilder.ToString(), maxChars);
	}

	private static (int start, int end) ParsePageRange(string pagesParam, int totalPages)
	{
		if (string.IsNullOrWhiteSpace(pagesParam))
		{
			return (start: 1, end: totalPages);
		}
		if (int.TryParse(pagesParam.Trim(), out var result))
		{
			return (start: Math.Max(1, result), end: Math.Min(result, totalPages));
		}
		string[] array = pagesParam.Split('-', StringSplitOptions.TrimEntries);
		if (array.Length == 2 && int.TryParse(array[0], out var result2) && int.TryParse(array[1], out var result3))
		{
			return (start: Math.Max(1, result2), end: Math.Min(result3, totalPages));
		}
		return (start: 1, end: totalPages);
	}

	private static string ExtractReferences(PdfDocument doc, int totalPages, int maxChars)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("=== References / Bibliography ===");
		stringBuilder.AppendLine();
		string[] array = new string[2] { "(?i)^\\s*(References|Bibliography|Works\\s+Cited|Literature\\s+Cited|참고\\s*문헌|참조|인용\\s*문헌)\\s*$", "(?i)^(References|Bibliography|참고문헌)\\s*\\n" };
		bool flag = false;
		int num = totalPages;
		while (num >= Math.Max(1, totalPages - 10) && !flag)
		{
			string text = doc.GetPage(num).Text;
			if (!string.IsNullOrWhiteSpace(text))
			{
				string[] array2 = array;
				foreach (string pattern in array2)
				{
					Match match = Regex.Match(text, pattern, RegexOptions.Multiline);
					if (!match.Success)
					{
						continue;
					}
					int index = match.Index;
					StringBuilder stringBuilder2 = stringBuilder;
					StringBuilder stringBuilder3 = stringBuilder2;
					StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(12, 1, stringBuilder2);
					handler.AppendLiteral("(Page ");
					handler.AppendFormatted(num);
					handler.AppendLiteral("부터 시작)");
					stringBuilder3.AppendLine(ref handler);
					string text2 = text;
					int num2 = index;
					stringBuilder.AppendLine(text2.Substring(num2, text2.Length - num2).Trim());
					for (int j = num + 1; j <= totalPages; j++)
					{
						if (stringBuilder.Length >= maxChars)
						{
							break;
						}
						string text3 = doc.GetPage(j).Text;
						if (!string.IsNullOrWhiteSpace(text3))
						{
							stringBuilder.AppendLine(text3.Trim());
						}
					}
					flag = true;
					break;
				}
			}
			num--;
		}
		if (!flag)
		{
			stringBuilder.AppendLine("(References 섹션 헤더를 찾지 못했습니다. 마지막 3페이지를 반환합니다.)");
			stringBuilder.AppendLine();
			for (int k = Math.Max(1, totalPages - 2); k <= totalPages; k++)
			{
				if (stringBuilder.Length >= maxChars)
				{
					break;
				}
				string text4 = doc.GetPage(k).Text;
				if (!string.IsNullOrWhiteSpace(text4))
				{
					StringBuilder stringBuilder2 = stringBuilder;
					StringBuilder stringBuilder4 = stringBuilder2;
					StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder2);
					handler.AppendLiteral("--- Page ");
					handler.AppendFormatted(k);
					handler.AppendLiteral(" ---");
					stringBuilder4.AppendLine(ref handler);
					stringBuilder.AppendLine(text4.Trim());
					stringBuilder.AppendLine();
				}
			}
		}
		string text5 = stringBuilder.ToString();
		List<string> list = ParseReferenceEntries(text5);
		if (list.Count > 0)
		{
			StringBuilder stringBuilder5 = new StringBuilder();
			StringBuilder stringBuilder2 = stringBuilder5;
			StringBuilder stringBuilder6 = stringBuilder2;
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(26, 1, stringBuilder2);
			handler.AppendLiteral("=== References (");
			handler.AppendFormatted(list.Count);
			handler.AppendLiteral("개 항목) ===\n");
			stringBuilder6.AppendLine(ref handler);
			for (int l = 0; l < list.Count; l++)
			{
				stringBuilder2 = stringBuilder5;
				StringBuilder stringBuilder7 = stringBuilder2;
				handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder2);
				handler.AppendLiteral("[");
				handler.AppendFormatted(l + 1);
				handler.AppendLiteral("] ");
				handler.AppendFormatted(list[l]);
				stringBuilder7.AppendLine(ref handler);
			}
			return Truncate(stringBuilder5.ToString(), maxChars);
		}
		return Truncate(text5, maxChars);
	}

	private static string ExtractAbstract(PdfDocument doc, int totalPages, int maxChars)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("=== Abstract ===");
		stringBuilder.AppendLine();
		for (int i = 1; i <= Math.Min(3, totalPages); i++)
		{
			string text = doc.GetPage(i).Text;
			if (!string.IsNullOrWhiteSpace(text))
			{
				Match match = Regex.Match(text, "(?i)(Abstract|초록|요약)\\s*\\n(.*?)(?=\\n\\s*(Keywords|Introduction|1\\.|서론|키워드|핵심어)\\s*[\\n:])", RegexOptions.Singleline);
				if (match.Success)
				{
					stringBuilder.AppendLine(match.Groups[2].Value.Trim());
					return Truncate(stringBuilder.ToString(), maxChars);
				}
			}
		}
		stringBuilder.AppendLine("(Abstract 섹션을 찾지 못했습니다. 첫 페이지를 반환합니다.)");
		string text2 = doc.GetPage(1).Text;
		if (!string.IsNullOrWhiteSpace(text2))
		{
			stringBuilder.AppendLine(text2.Trim());
		}
		return Truncate(stringBuilder.ToString(), maxChars);
	}

	private static List<string> ParseReferenceEntries(string text)
	{
		List<string> list = new List<string>();
		string[] array = Regex.Split(text, "\\n\\s*\\[(\\d+)\\]\\s*");
		if (array.Length > 3)
		{
			for (int i = 2; i < array.Length; i += 2)
			{
				string text2 = array[i].Trim().Replace("\n", " ").Replace("  ", " ");
				if (text2.Length > 10)
				{
					list.Add(text2);
				}
			}
			return list;
		}
		string[] array2 = Regex.Split(text, "\\n\\s*(\\d+)\\.\\s+");
		if (array2.Length > 5)
		{
			for (int j = 2; j < array2.Length; j += 2)
			{
				string text3 = array2[j].Trim().Replace("\n", " ").Replace("  ", " ");
				if (text3.Length > 10)
				{
					list.Add(text3);
				}
			}
			return list;
		}
		return list;
	}

	private static string ReadBibTeX(string path, int maxChars)
	{
		string text = File.ReadAllText(path, Encoding.UTF8);
		StringBuilder stringBuilder = new StringBuilder();
		Regex regex = new Regex("@(\\w+)\\s*\\{\\s*([^,\\s]+)\\s*,\\s*(.*?)\\n\\s*\\}", RegexOptions.Singleline);
		Regex regex2 = new Regex("(\\w+)\\s*=\\s*[\\{\"](.*?)[\\}\"]", RegexOptions.Singleline);
		MatchCollection matchCollection = regex.Matches(text);
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder3 = stringBuilder2;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(12, 1, stringBuilder2);
		handler.AppendLiteral("BibTeX: ");
		handler.AppendFormatted(matchCollection.Count);
		handler.AppendLiteral("개 항목");
		stringBuilder3.AppendLine(ref handler);
		stringBuilder.AppendLine();
		int num = 0;
		foreach (Match item in matchCollection)
		{
			if (stringBuilder.Length >= maxChars)
			{
				break;
			}
			num++;
			string value = item.Groups[1].Value;
			string value2 = item.Groups[2].Value;
			string value3 = item.Groups[3].Value;
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder4 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(6, 3, stringBuilder2);
			handler.AppendLiteral("[");
			handler.AppendFormatted(num);
			handler.AppendLiteral("] @");
			handler.AppendFormatted(value);
			handler.AppendLiteral("{");
			handler.AppendFormatted(value2);
			handler.AppendLiteral("}");
			stringBuilder4.AppendLine(ref handler);
			MatchCollection matchCollection2 = regex2.Matches(value3);
			foreach (Match item2 in matchCollection2)
			{
				string text2 = item2.Groups[1].Value.ToLower();
				string value4 = item2.Groups[2].Value.Trim();
				bool flag;
				switch (text2)
				{
				case "author":
				case "title":
				case "journal":
				case "booktitle":
				case "year":
				case "volume":
				case "number":
				case "pages":
				case "doi":
				case "publisher":
				case "url":
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				if (flag)
				{
					stringBuilder2 = stringBuilder;
					StringBuilder stringBuilder5 = stringBuilder2;
					handler = new StringBuilder.AppendInterpolatedStringHandler(4, 2, stringBuilder2);
					handler.AppendLiteral("  ");
					handler.AppendFormatted(text2);
					handler.AppendLiteral(": ");
					handler.AppendFormatted(value4);
					stringBuilder5.AppendLine(ref handler);
				}
			}
			stringBuilder.AppendLine();
		}
		if (matchCollection.Count == 0)
		{
			stringBuilder.AppendLine("(BibTeX 항목을 파싱하지 못했습니다. 원문을 반환합니다.)");
			stringBuilder.AppendLine(Truncate(text, maxChars - stringBuilder.Length));
		}
		return Truncate(stringBuilder.ToString(), maxChars);
	}

	private static string ReadRis(string path, int maxChars)
	{
		string[] array = File.ReadAllLines(path, Encoding.UTF8);
		StringBuilder stringBuilder = new StringBuilder();
		List<Dictionary<string, List<string>>> list = new List<Dictionary<string, List<string>>>();
		Dictionary<string, List<string>> dictionary = null;
		string[] array2 = array;
		string key2;
		foreach (string text in array2)
		{
			if (text.StartsWith("TY  -"))
			{
				dictionary = new Dictionary<string, List<string>>();
				list.Add(dictionary);
			}
			else if (text.StartsWith("ER  -"))
			{
				dictionary = null;
				continue;
			}
			if (dictionary != null && text.Length >= 6 && text[2] == ' ' && text[3] == ' ' && text[4] == '-' && text[5] == ' ')
			{
				string key = text.Substring(0, 2).Trim();
				key2 = text;
				string item = key2.Substring(6, key2.Length - 6).Trim();
				if (!dictionary.ContainsKey(key))
				{
					dictionary[key] = new List<string>();
				}
				dictionary[key].Add(item);
			}
		}
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder3 = stringBuilder2;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
		handler.AppendLiteral("RIS: ");
		handler.AppendFormatted(list.Count);
		handler.AppendLiteral("개 항목");
		stringBuilder3.AppendLine(ref handler);
		stringBuilder.AppendLine();
		Dictionary<string, string> dictionary2 = new Dictionary<string, string>
		{
			["TY"] = "Type",
			["AU"] = "Author",
			["TI"] = "Title",
			["T1"] = "Title",
			["JO"] = "Journal",
			["JF"] = "Journal",
			["PY"] = "Year",
			["Y1"] = "Year",
			["VL"] = "Volume",
			["IS"] = "Issue",
			["SP"] = "Start Page",
			["EP"] = "End Page",
			["DO"] = "DOI",
			["UR"] = "URL",
			["PB"] = "Publisher",
			["AB"] = "Abstract",
			["KW"] = "Keyword",
			["SN"] = "ISSN/ISBN"
		};
		for (int j = 0; j < list.Count; j++)
		{
			if (stringBuilder.Length >= maxChars)
			{
				break;
			}
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder4 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2);
			handler.AppendLiteral("[");
			handler.AppendFormatted(j + 1);
			handler.AppendLiteral("]");
			stringBuilder4.AppendLine(ref handler);
			Dictionary<string, List<string>> dictionary3 = list[j];
			foreach (KeyValuePair<string, List<string>> item2 in dictionary3)
			{
				item2.Deconstruct(out key2, out var value);
				string text2 = key2;
				List<string> values = value;
				string valueOrDefault = dictionary2.GetValueOrDefault(text2, text2);
				if ((text2 == "AU" || text2 == "KW") ? true : false)
				{
					stringBuilder2 = stringBuilder;
					StringBuilder stringBuilder5 = stringBuilder2;
					handler = new StringBuilder.AppendInterpolatedStringHandler(4, 2, stringBuilder2);
					handler.AppendLiteral("  ");
					handler.AppendFormatted(valueOrDefault);
					handler.AppendLiteral(": ");
					handler.AppendFormatted(string.Join("; ", values));
					stringBuilder5.AppendLine(ref handler);
				}
				else
				{
					stringBuilder2 = stringBuilder;
					StringBuilder stringBuilder6 = stringBuilder2;
					handler = new StringBuilder.AppendInterpolatedStringHandler(4, 2, stringBuilder2);
					handler.AppendLiteral("  ");
					handler.AppendFormatted(valueOrDefault);
					handler.AppendLiteral(": ");
					handler.AppendFormatted(string.Join(" ", values));
					stringBuilder6.AppendLine(ref handler);
				}
			}
			stringBuilder.AppendLine();
		}
		return Truncate(stringBuilder.ToString(), maxChars);
	}

	private static string ReadDocx(string path, int maxChars)
	{
		StringBuilder stringBuilder = new StringBuilder();
		using WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(path, isEditable: false);
		Body body = wordprocessingDocument.MainDocumentPart?.Document.Body;
		if (body == null)
		{
			return "(빈 문서)";
		}
		foreach (Paragraph item in body.Elements<Paragraph>())
		{
			string innerText = item.InnerText;
			if (!string.IsNullOrWhiteSpace(innerText))
			{
				stringBuilder.AppendLine(innerText);
				if (stringBuilder.Length >= maxChars)
				{
					break;
				}
			}
		}
		return Truncate(stringBuilder.ToString(), maxChars);
	}

	private static string ReadXlsx(string path, string sheetParam, int maxChars)
	{
		StringBuilder stringBuilder = new StringBuilder();
		using SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, isEditable: false);
		WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
		if (workbookPart == null)
		{
			return "(빈 스프레드시트)";
		}
		List<Sheet> list = workbookPart.Workbook.Sheets?.Elements<Sheet>().ToList() ?? new List<Sheet>();
		if (list.Count == 0)
		{
			return "(시트 없음)";
		}
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder3 = stringBuilder2;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(14, 2, stringBuilder2);
		handler.AppendLiteral("Excel: ");
		handler.AppendFormatted(list.Count);
		handler.AppendLiteral("개 시트 (");
		handler.AppendFormatted(string.Join(", ", list.Select((Sheet s) => s.Name?.Value)));
		handler.AppendLiteral(")");
		stringBuilder3.AppendLine(ref handler);
		stringBuilder.AppendLine();
		Sheet sheet = null;
		if (!string.IsNullOrEmpty(sheetParam))
		{
			sheet = ((!int.TryParse(sheetParam, out var result) || result < 1 || result > list.Count) ? list.FirstOrDefault((Sheet s) => string.Equals(s.Name?.Value, sheetParam, StringComparison.OrdinalIgnoreCase)) : list[result - 1]);
		}
		if (sheet == null)
		{
			sheet = list[0];
		}
		string text = sheet.Id?.Value;
		if (text == null)
		{
			return "(시트 ID 없음)";
		}
		WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(text);
		List<SharedStringItem> sharedStrings = workbookPart.SharedStringTablePart?.SharedStringTable.Elements<SharedStringItem>().ToList() ?? new List<SharedStringItem>();
		List<Row> list2 = worksheetPart.Worksheet.Descendants<Row>().ToList();
		stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder4 = stringBuilder2;
		handler = new StringBuilder.AppendInterpolatedStringHandler(10, 2, stringBuilder2);
		handler.AppendLiteral("[");
		handler.AppendFormatted(sheet.Name?.Value);
		handler.AppendLiteral("] (");
		handler.AppendFormatted(list2.Count);
		handler.AppendLiteral(" rows)");
		stringBuilder4.AppendLine(ref handler);
		foreach (Row item in list2)
		{
			List<Cell> list3 = item.Elements<Cell>().ToList();
			List<string> list4 = new List<string>();
			foreach (Cell item2 in list3)
			{
				list4.Add(GetCellValue(item2, sharedStrings));
			}
			stringBuilder.AppendLine(string.Join("\t", list4));
			if (stringBuilder.Length >= maxChars)
			{
				break;
			}
		}
		return Truncate(stringBuilder.ToString(), maxChars);
	}

	private static string GetCellValue(Cell cell, List<SharedStringItem> sharedStrings)
	{
		string text = cell.CellValue?.Text ?? "";
		CellValues? cellValues = cell.DataType?.Value;
		CellValues sharedString = CellValues.SharedString;
		if (cellValues.HasValue && cellValues.GetValueOrDefault() == sharedString && int.TryParse(text, out var result) && result >= 0 && result < sharedStrings.Count)
		{
			return sharedStrings[result].InnerText;
		}
		return text;
	}

	private static async Task<string> ReadTextFile(string path, int maxChars, CancellationToken ct)
	{
		return Truncate(await File.ReadAllTextAsync(path, Encoding.UTF8, ct), maxChars);
	}

	private static string Truncate(string text, int maxChars)
	{
		if (text.Length <= maxChars)
		{
			return text;
		}
		return text.Substring(0, maxChars) + "\n\n... (내용 잘림 — pages 또는 section 파라미터로 특정 부분을 읽을 수 있습니다)";
	}

	private static string FormatSize(long bytes)
	{
		if (1 == 0)
		{
		}
		string result = ((bytes < 1024) ? $"{bytes} B" : ((bytes >= 1048576) ? $"{(double)bytes / 1048576.0:F1} MB" : $"{(double)bytes / 1024.0:F1} KB"));
		if (1 == 0)
		{
		}
		return result;
	}

	private static int GetIntValue(JsonElement el, int defaultValue)
	{
		if (el.ValueKind == JsonValueKind.Number)
		{
			return el.GetInt32();
		}
		if (el.ValueKind == JsonValueKind.String && int.TryParse(el.GetString(), out var result))
		{
			return result;
		}
		return defaultValue;
	}
}
