using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace AxCopilot.Services.Agent;

public class DocxSkill : IAgentTool
{
	public string Name => "docx_create";

	public string Description => "Create a rich Word (.docx) document. Supports: sections with heading+body, tables with optional header styling, text formatting (bold, italic, color, highlight, shading), headers/footers with page numbers, page breaks between sections, and numbered/bulleted lists.";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema obj = new ToolParameterSchema
			{
				Properties = new Dictionary<string, ToolProperty>
				{
					["path"] = new ToolProperty
					{
						Type = "string",
						Description = "Output file path (.docx). Relative to work folder."
					},
					["title"] = new ToolProperty
					{
						Type = "string",
						Description = "Document title (optional)."
					},
					["sections"] = new ToolProperty
					{
						Type = "array",
						Description = "Array of content blocks. Each block is one of:\n• Section: {\"heading\": \"...\", \"body\": \"...\", \"level\": 1|2}\n• Table: {\"type\": \"table\", \"headers\": [\"A\",\"B\"], \"rows\": [[\"1\",\"2\"]], \"style\": \"striped|plain\"}\n• PageBreak: {\"type\": \"pagebreak\"}\n• List: {\"type\": \"list\", \"style\": \"bullet|number\", \"items\": [\"item1\", \"item2\"]}\nBody text supports inline formatting: **bold**, *italic*, `code`.",
						Items = new ToolProperty
						{
							Type = "object"
						}
					},
					["header"] = new ToolProperty
					{
						Type = "string",
						Description = "Header text shown at top of every page (optional)."
					},
					["footer"] = new ToolProperty
					{
						Type = "string",
						Description = "Footer text. Use {page} for page number. Default: 'AX Copilot · {page}' if header is set."
					},
					["page_numbers"] = new ToolProperty
					{
						Type = "boolean",
						Description = "Show page numbers in footer. Default: true if header or footer is set."
					}
				}
			};
			int num = 2;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			Span<string> span = CollectionsMarshal.AsSpan(list);
			span[0] = "path";
			span[1] = "sections";
			obj.Required = list;
			return obj;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
	{
		string path = args.GetProperty("path").GetString() ?? "";
		JsonElement t;
		string title = (args.TryGetProperty("title", out t) ? (t.GetString() ?? "") : "");
		JsonElement hdr;
		string headerText = (args.TryGetProperty("header", out hdr) ? hdr.GetString() : null);
		JsonElement ftr;
		string footerText = (args.TryGetProperty("footer", out ftr) ? ftr.GetString() : null);
		JsonElement pn;
		bool showPageNumbers = (args.TryGetProperty("page_numbers", out pn) ? pn.GetBoolean() : (headerText != null || footerText != null));
		string fullPath = FileReadTool.ResolvePath(path, context.WorkFolder);
		if (context.ActiveTab == "Cowork")
		{
			fullPath = AgentContext.EnsureTimestampedPath(fullPath);
		}
		if (!fullPath.EndsWith(".docx", StringComparison.OrdinalIgnoreCase))
		{
			fullPath += ".docx";
		}
		if (!context.IsPathAllowed(fullPath))
		{
			return ToolResult.Fail("경로 접근 차단: " + fullPath);
		}
		if (!(await context.CheckWritePermissionAsync(Name, fullPath)))
		{
			return ToolResult.Fail("쓰기 권한 거부: " + fullPath);
		}
		try
		{
			JsonElement sections = args.GetProperty("sections");
			string dir = Path.GetDirectoryName(fullPath);
			if (!string.IsNullOrEmpty(dir))
			{
				Directory.CreateDirectory(dir);
			}
			using WordprocessingDocument doc = WordprocessingDocument.Create(fullPath, WordprocessingDocumentType.Document);
			MainDocumentPart mainPart = doc.AddMainDocumentPart();
			mainPart.Document = new Document();
			Body body = mainPart.Document.AppendChild(new Body());
			if (headerText != null || footerText != null || showPageNumbers)
			{
				AddHeaderFooter(mainPart, body, headerText, footerText, showPageNumbers);
			}
			if (!string.IsNullOrEmpty(title))
			{
				body.Append(CreateTitleParagraph(title));
				body.Append(new Paragraph(new ParagraphProperties
				{
					ParagraphBorders = new ParagraphBorders(new BottomBorder
					{
						Val = BorderValues.Single,
						Size = 6u,
						Color = "4472C4",
						Space = 1u
					}),
					SpacingBetweenLines = new SpacingBetweenLines
					{
						After = "300"
					}
				}));
			}
			int sectionCount = 0;
			int tableCount = 0;
			foreach (JsonElement section in sections.EnumerateArray())
			{
				JsonElement bt;
				switch ((!section.TryGetProperty("type", out bt)) ? null : bt.GetString()?.ToLower())
				{
				case "pagebreak":
					body.Append(CreatePageBreak());
					continue;
				case "table":
					body.Append(CreateTable(section));
					tableCount++;
					continue;
				case "list":
					AppendList(body, section);
					continue;
				}
				JsonElement h;
				string heading = (section.TryGetProperty("heading", out h) ? (h.GetString() ?? "") : "");
				JsonElement b;
				string bodyText = (section.TryGetProperty("body", out b) ? (b.GetString() ?? "") : "");
				JsonElement lv;
				int level = ((!section.TryGetProperty("level", out lv)) ? 1 : lv.GetInt32());
				if (!string.IsNullOrEmpty(heading))
				{
					body.Append(CreateHeadingParagraph(heading, level));
				}
				if (!string.IsNullOrEmpty(bodyText))
				{
					string[] array = bodyText.Split('\n');
					foreach (string line in array)
					{
						body.Append(CreateBodyParagraph(line));
					}
				}
				sectionCount++;
				bt = default(JsonElement);
				h = default(JsonElement);
				b = default(JsonElement);
				lv = default(JsonElement);
			}
			mainPart.Document.Save();
			List<string> parts = new List<string>();
			if (!string.IsNullOrEmpty(title))
			{
				parts.Add("제목: " + title);
			}
			if (sectionCount > 0)
			{
				parts.Add($"섹션: {sectionCount}개");
			}
			if (tableCount > 0)
			{
				parts.Add($"테이블: {tableCount}개");
			}
			if (headerText != null)
			{
				parts.Add("머리글");
			}
			if (showPageNumbers)
			{
				parts.Add("페이지번호");
			}
			return ToolResult.Ok("Word 문서 생성 완료: " + fullPath + "\n" + string.Join(", ", parts), fullPath);
		}
		catch (Exception ex)
		{
			return ToolResult.Fail("Word 문서 생성 실패: " + ex.Message);
		}
	}

	private static Paragraph CreateTitleParagraph(string text)
	{
		Paragraph paragraph = new Paragraph();
		paragraph.ParagraphProperties = new ParagraphProperties
		{
			Justification = new Justification
			{
				Val = JustificationValues.Center
			},
			SpacingBetweenLines = new SpacingBetweenLines
			{
				After = "100"
			}
		};
		Run run = new Run(new Text(text));
		run.RunProperties = new RunProperties
		{
			Bold = new Bold(),
			FontSize = new FontSize
			{
				Val = "44"
			},
			Color = new Color
			{
				Val = "1F3864"
			}
		};
		paragraph.Append(run);
		return paragraph;
	}

	private static Paragraph CreateHeadingParagraph(string text, int level)
	{
		Paragraph paragraph = new Paragraph();
		string text2 = ((level <= 1) ? "32" : "26");
		string text3 = ((level <= 1) ? "2E74B5" : "404040");
		paragraph.ParagraphProperties = new ParagraphProperties
		{
			SpacingBetweenLines = new SpacingBetweenLines
			{
				Before = ((level <= 1) ? "360" : "240"),
				After = "120"
			}
		};
		if (level <= 1)
		{
			paragraph.ParagraphProperties.ParagraphBorders = new ParagraphBorders(new BottomBorder
			{
				Val = BorderValues.Single,
				Size = 4u,
				Color = "B4C6E7",
				Space = 1u
			});
		}
		Run run = new Run(new Text(text));
		run.RunProperties = new RunProperties
		{
			Bold = new Bold(),
			FontSize = new FontSize
			{
				Val = text2
			},
			Color = new Color
			{
				Val = text3
			}
		};
		paragraph.Append(run);
		return paragraph;
	}

	private static Paragraph CreateBodyParagraph(string text)
	{
		Paragraph paragraph = new Paragraph();
		paragraph.ParagraphProperties = new ParagraphProperties
		{
			SpacingBetweenLines = new SpacingBetweenLines
			{
				Line = "360"
			}
		};
		AppendFormattedRuns(paragraph, text);
		return paragraph;
	}

	private static void AppendFormattedRuns(Paragraph para, string text)
	{
		Regex regex = new Regex("\\*\\*(.+?)\\*\\*|\\*(.+?)\\*|`(.+?)`");
		int num = 0;
		foreach (Match item in regex.Matches(text))
		{
			if (item.Index > num)
			{
				OpenXmlElement[] array = new OpenXmlElement[1];
				int num2 = num;
				array[0] = CreateRun(text.Substring(num2, item.Index - num2));
				para.Append(array);
			}
			if (item.Groups[1].Success)
			{
				Run run = CreateRun(item.Groups[1].Value);
				Run run2 = run;
				if (run2.RunProperties == null)
				{
					RunProperties runProperties = (run2.RunProperties = new RunProperties());
				}
				run.RunProperties.Bold = new Bold();
				para.Append(run);
			}
			else if (item.Groups[2].Success)
			{
				Run run3 = CreateRun(item.Groups[2].Value);
				Run run2 = run3;
				if (run2.RunProperties == null)
				{
					RunProperties runProperties = (run2.RunProperties = new RunProperties());
				}
				run3.RunProperties.Italic = new Italic();
				para.Append(run3);
			}
			else if (item.Groups[3].Success)
			{
				Run run4 = CreateRun(item.Groups[3].Value);
				Run run2 = run4;
				if (run2.RunProperties == null)
				{
					RunProperties runProperties = (run2.RunProperties = new RunProperties());
				}
				run4.RunProperties.RunFonts = new RunFonts
				{
					Ascii = "Consolas",
					HighAnsi = "Consolas"
				};
				run4.RunProperties.FontSize = new FontSize
				{
					Val = "20"
				};
				run4.RunProperties.Shading = new Shading
				{
					Val = ShadingPatternValues.Clear,
					Fill = "F2F2F2",
					Color = "auto"
				};
				para.Append(run4);
			}
			num = item.Index + item.Length;
		}
		if (num < text.Length)
		{
			OpenXmlElement[] array2 = new OpenXmlElement[1];
			int num2 = num;
			array2[0] = CreateRun(text.Substring(num2, text.Length - num2));
			para.Append(array2);
		}
		if (num == 0 && text.Length == 0)
		{
			para.Append(CreateRun(""));
		}
	}

	private static Run CreateRun(string text)
	{
		Run run = new Run(new Text(text)
		{
			Space = SpaceProcessingModeValues.Preserve
		});
		run.RunProperties = new RunProperties
		{
			FontSize = new FontSize
			{
				Val = "22"
			}
		};
		return run;
	}

	private static Table CreateTable(JsonElement section)
	{
		JsonElement value;
		JsonElement jsonElement = (section.TryGetProperty("headers", out value) ? value : default(JsonElement));
		JsonElement value2;
		JsonElement jsonElement2 = (section.TryGetProperty("rows", out value2) ? value2 : default(JsonElement));
		JsonElement value3;
		string text = (section.TryGetProperty("style", out value3) ? (value3.GetString() ?? "striped") : "striped");
		Table table = new Table();
		TableProperties newChild = new TableProperties(new TableBorders(new TopBorder
		{
			Val = BorderValues.Single,
			Size = 4u,
			Color = "D9D9D9"
		}, new BottomBorder
		{
			Val = BorderValues.Single,
			Size = 4u,
			Color = "D9D9D9"
		}, new LeftBorder
		{
			Val = BorderValues.Single,
			Size = 4u,
			Color = "D9D9D9"
		}, new RightBorder
		{
			Val = BorderValues.Single,
			Size = 4u,
			Color = "D9D9D9"
		}, new InsideHorizontalBorder
		{
			Val = BorderValues.Single,
			Size = 4u,
			Color = "D9D9D9"
		}, new InsideVerticalBorder
		{
			Val = BorderValues.Single,
			Size = 4u,
			Color = "D9D9D9"
		}), new TableWidth
		{
			Width = "5000",
			Type = TableWidthUnitValues.Pct
		});
		table.AppendChild(newChild);
		if (jsonElement.ValueKind == JsonValueKind.Array)
		{
			TableRow tableRow = new TableRow();
			foreach (JsonElement item in jsonElement.EnumerateArray())
			{
				TableCell tableCell = new TableCell();
				tableCell.TableCellProperties = new TableCellProperties
				{
					Shading = new Shading
					{
						Val = ShadingPatternValues.Clear,
						Fill = "2E74B5",
						Color = "auto"
					},
					TableCellVerticalAlignment = new TableCellVerticalAlignment
					{
						Val = TableVerticalAlignmentValues.Center
					}
				};
				Paragraph paragraph = new Paragraph(new Run(new Text(item.GetString() ?? ""))
				{
					RunProperties = new RunProperties
					{
						Bold = new Bold(),
						FontSize = new FontSize
						{
							Val = "20"
						},
						Color = new Color
						{
							Val = "FFFFFF"
						}
					}
				});
				paragraph.ParagraphProperties = new ParagraphProperties
				{
					SpacingBetweenLines = new SpacingBetweenLines
					{
						Before = "40",
						After = "40"
					}
				};
				tableCell.Append(paragraph);
				tableRow.Append(tableCell);
			}
			table.Append(tableRow);
		}
		if (jsonElement2.ValueKind == JsonValueKind.Array)
		{
			int num = 0;
			foreach (JsonElement item2 in jsonElement2.EnumerateArray())
			{
				TableRow tableRow2 = new TableRow();
				foreach (JsonElement item3 in item2.EnumerateArray())
				{
					TableCell tableCell2 = new TableCell();
					if (text == "striped" && num % 2 == 0)
					{
						tableCell2.TableCellProperties = new TableCellProperties
						{
							Shading = new Shading
							{
								Val = ShadingPatternValues.Clear,
								Fill = "F2F7FB",
								Color = "auto"
							}
						};
					}
					Paragraph paragraph2 = new Paragraph(new Run(new Text(item3.ToString())
					{
						Space = SpaceProcessingModeValues.Preserve
					})
					{
						RunProperties = new RunProperties
						{
							FontSize = new FontSize
							{
								Val = "20"
							}
						}
					});
					paragraph2.ParagraphProperties = new ParagraphProperties
					{
						SpacingBetweenLines = new SpacingBetweenLines
						{
							Before = "20",
							After = "20"
						}
					};
					tableCell2.Append(paragraph2);
					tableRow2.Append(tableCell2);
				}
				table.Append(tableRow2);
				num++;
			}
		}
		return table;
	}

	private static void AppendList(Body body, JsonElement section)
	{
		JsonElement value;
		JsonElement jsonElement = (section.TryGetProperty("items", out value) ? value : default(JsonElement));
		JsonElement value2;
		string text = (section.TryGetProperty("style", out value2) ? (value2.GetString() ?? "bullet") : "bullet");
		if (jsonElement.ValueKind != JsonValueKind.Array)
		{
			return;
		}
		int num = 1;
		foreach (JsonElement item in jsonElement.EnumerateArray())
		{
			string text2 = item.GetString() ?? item.ToString();
			string text3 = ((text == "number") ? $"{num}. " : "• ");
			Paragraph paragraph = new Paragraph();
			paragraph.ParagraphProperties = new ParagraphProperties
			{
				Indentation = new Indentation
				{
					Left = "720"
				},
				SpacingBetweenLines = new SpacingBetweenLines
				{
					Line = "320"
				}
			};
			Run run = new Run(new Text(text3)
			{
				Space = SpaceProcessingModeValues.Preserve
			});
			run.RunProperties = new RunProperties
			{
				FontSize = new FontSize
				{
					Val = "22"
				},
				Bold = ((text == "number") ? new Bold() : null)
			};
			paragraph.Append(run);
			Run run2 = new Run(new Text(text2)
			{
				Space = SpaceProcessingModeValues.Preserve
			});
			run2.RunProperties = new RunProperties
			{
				FontSize = new FontSize
				{
					Val = "22"
				}
			};
			paragraph.Append(run2);
			body.Append(paragraph);
			num++;
		}
	}

	private static Paragraph CreatePageBreak()
	{
		Paragraph paragraph = new Paragraph();
		Run run = new Run(new Break
		{
			Type = BreakValues.Page
		});
		paragraph.Append(run);
		return paragraph;
	}

	private static void AddHeaderFooter(MainDocumentPart mainPart, Body body, string? headerText, string? footerText, bool showPageNumbers)
	{
		if (!string.IsNullOrEmpty(headerText))
		{
			HeaderPart headerPart = mainPart.AddNewPart<HeaderPart>();
			Header header = new Header();
			Paragraph paragraph = new Paragraph(new Run(new Text(headerText))
			{
				RunProperties = new RunProperties
				{
					FontSize = new FontSize
					{
						Val = "18"
					},
					Color = new Color
					{
						Val = "808080"
					}
				}
			});
			paragraph.ParagraphProperties = new ParagraphProperties
			{
				Justification = new Justification
				{
					Val = JustificationValues.Right
				}
			};
			header.Append(paragraph);
			headerPart.Header = header;
			SectionProperties sectionProperties = body.GetFirstChild<SectionProperties>() ?? body.AppendChild(new SectionProperties());
			sectionProperties.Append(new HeaderReference
			{
				Type = HeaderFooterValues.Default,
				Id = mainPart.GetIdOfPart(headerPart)
			});
		}
		if (!(!string.IsNullOrEmpty(footerText) || showPageNumbers))
		{
			return;
		}
		FooterPart footerPart = mainPart.AddNewPart<FooterPart>();
		Footer footer = new Footer();
		Paragraph paragraph2 = new Paragraph();
		paragraph2.ParagraphProperties = new ParagraphProperties
		{
			Justification = new Justification
			{
				Val = JustificationValues.Center
			}
		};
		string text = footerText ?? "AX Copilot";
		if (showPageNumbers)
		{
			if (text.Contains("{page}"))
			{
				string[] array = text.Split("{page}");
				paragraph2.Append(CreateFooterRun(array[0]));
				paragraph2.Append(CreatePageNumberRun());
				if (array.Length > 1)
				{
					paragraph2.Append(CreateFooterRun(array[1]));
				}
			}
			else
			{
				paragraph2.Append(CreateFooterRun(text + " · "));
				paragraph2.Append(CreatePageNumberRun());
			}
		}
		else
		{
			paragraph2.Append(CreateFooterRun(text));
		}
		footer.Append(paragraph2);
		footerPart.Footer = footer;
		SectionProperties sectionProperties2 = body.GetFirstChild<SectionProperties>() ?? body.AppendChild(new SectionProperties());
		sectionProperties2.Append(new FooterReference
		{
			Type = HeaderFooterValues.Default,
			Id = mainPart.GetIdOfPart(footerPart)
		});
	}

	private static Run CreateFooterRun(string text)
	{
		return new Run(new Text(text)
		{
			Space = SpaceProcessingModeValues.Preserve
		})
		{
			RunProperties = new RunProperties
			{
				FontSize = new FontSize
				{
					Val = "16"
				},
				Color = new Color
				{
					Val = "999999"
				}
			}
		};
	}

	private static Run CreatePageNumberRun()
	{
		Run run = new Run();
		run.RunProperties = new RunProperties
		{
			FontSize = new FontSize
			{
				Val = "16"
			},
			Color = new Color
			{
				Val = "999999"
			}
		};
		run.Append(new FieldChar
		{
			FieldCharType = FieldCharValues.Begin
		});
		run.Append(new FieldCode(" PAGE ")
		{
			Space = SpaceProcessingModeValues.Preserve
		});
		run.Append(new FieldChar
		{
			FieldCharType = FieldCharValues.End
		});
		return run;
	}
}
