using DocumentAutomation.Shared; namespace DocxWorkbench.Core; public interface IDocumentInspector { DocumentInspectionResult Inspect(string docxPath); } public sealed class UnsupportedDocumentFormatException(string message) : InvalidOperationException(message); public enum WorkbenchState { Empty, InputsSelected, Analyzed, ReadyToBuild, Building, BuildSucceeded, BuildFailed, Validating, ValidationSucceeded, ValidationFailed } public sealed class WorkbenchStateController { public WorkbenchState State { get; private set; } = WorkbenchState.Empty; public bool CanBuild => State == WorkbenchState.ReadyToBuild; public bool CanValidate => State == WorkbenchState.BuildSucceeded; public bool CanOpenDocx { get; private set; } public bool CanOpenPdf { get; private set; } public void InputsSelected(bool selected) { State = selected ? WorkbenchState.InputsSelected : WorkbenchState.Empty; if (!selected) ClearArtifacts(); } public void AnalysisCompleted(bool templateOk, bool bodyOk, bool insertionPointFound) { State = templateOk && bodyOk && insertionPointFound ? WorkbenchState.ReadyToBuild : WorkbenchState.Analyzed; ClearArtifacts(); } public void BeginBuild() { if (!CanBuild) throw new InvalidOperationException("Build is unavailable in the current state."); State = WorkbenchState.Building; ClearArtifacts(); } public void BuildSucceeded() { State = WorkbenchState.BuildSucceeded; CanOpenDocx = true; CanOpenPdf = true; } public void BuildFailed() { State = WorkbenchState.BuildFailed; ClearArtifacts(); } public void BeginValidation() { if (!CanValidate) throw new InvalidOperationException("Validation is available only after a successful build."); State = WorkbenchState.Validating; } public void ValidationCompleted(bool success) => State = success ? WorkbenchState.ValidationSucceeded : WorkbenchState.ValidationFailed; private void ClearArtifacts() { CanOpenDocx = false; CanOpenPdf = false; } } public interface IWordDocumentService { Task BuildDocumentAsync(WordDocumentBuildRequest request, IProgress? progress, CancellationToken cancellationToken); Task ExportPdfAsync(string docxPath, string outputPdfPath, CancellationToken cancellationToken); } public interface IDocxBodyImporter { Task ImportAsync(DocxBodyImportRequest request, IProgress? progress, CancellationToken cancellationToken); } public interface IWordDocumentNormalizer { Task NormalizeAsync(DocumentNormalizationRequest request, IProgress? progress, CancellationToken cancellationToken); } public interface ITemplateLearningService { Task LearnAsync(TemplateLearningRequest request, IProgress? progress, CancellationToken cancellationToken); } public interface ITemplatePackageStore { void Save(string packagePath, LearnedTemplate template, string normalizedDocxPath); (LearnedTemplate Template, string TemplateDocxPath, string WorkingDirectory) Open(string packagePath); } public interface ITemplateDocumentGenerator { Task GenerateAsync(TemplateGenerationRequest request, IProgress? progress, CancellationToken cancellationToken); } public static class DocumentInputValidation { public static void Validate(WordDocumentBuildRequest request) { if (!File.Exists(request.TemplatePath)) throw new FileNotFoundException("Шаблон DOCX не найден.", request.TemplatePath); if (!File.Exists(request.BodyPath)) throw new FileNotFoundException("Файл тела документа не найден.", request.BodyPath); if (!request.TemplatePath.EndsWith(".docx", StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("Шаблон должен быть DOCX."); if (Path.GetFullPath(request.TemplatePath).Equals(Path.GetFullPath(request.OutputDocxPath), StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("Нельзя перезаписывать исходный шаблон."); Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(request.OutputDocxPath))!); } }