using System;
namespace D365MetadataBridge.Services
{
///
/// Pure, dependency-free helpers for the form authoring surface (create/add-control).
///
/// Deliberately references NO Microsoft.Dynamics.* type so it can be compiled and
/// unit-tested away from a D365FO installation — see
/// tests/bridge/formAuthoringDefaults.test.ts, which compiles THIS FILE with the
/// plain .NET SDK and asserts the behaviour below without an AOS or a metadata model.
///
internal static class FormAuthoringDefaults
{
/// Name of the method that carries a form's class declaration in AxForm XML.
public const string ClassDeclarationMethodName = "classDeclaration";
/// True when denotes the form class declaration.
public static bool IsClassDeclarationMethod(string? methodName)
=> methodName != null
&& string.Equals(methodName.Trim(), ClassDeclarationMethodName, StringComparison.OrdinalIgnoreCase);
///
/// True when addresses the form DESIGN ROOT rather than a
/// named control inside it.
///
/// Why this exists: AddControl used to resolve its parent purely with
/// FindControlRecursive(design, parentControl), which only ever walks design.Controls and
/// therefore can NEVER return the design itself. On a form whose design has no controls yet,
/// every possible parentControl value failed by construction, so a form could never receive
/// its FIRST top-level control (corpus: 2026-07-21T18__L2-form-modify-controls__c262b19).
///
/// Recognised sentinels: null / empty / whitespace, "Design", "FormDesign", "Root",
/// the form name itself, and "<FormName>Design" — i.e. every spelling an agent
/// plausibly reaches for when it wants "put this at the top level".
///
public static bool IsDesignRootSentinel(string? parentControl, string? formName)
{
if (string.IsNullOrWhiteSpace(parentControl)) return true;
var p = parentControl!.Trim();
if (string.Equals(p, "Design", StringComparison.OrdinalIgnoreCase)) return true;
if (string.Equals(p, "FormDesign", StringComparison.OrdinalIgnoreCase)) return true;
if (string.Equals(p, "Root", StringComparison.OrdinalIgnoreCase)) return true;
if (!string.IsNullOrWhiteSpace(formName))
{
var f = formName!.Trim();
if (string.Equals(p, f, StringComparison.OrdinalIgnoreCase)) return true;
if (string.Equals(p, f + "Design", StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
///
/// The class declaration every AxForm must carry. Without it xppc fails with
/// "The 'classDeclaration' is missing from element '<Form>'", i.e. a bridge-created
/// form was dead on arrival (corpus: 2026-07-21T18__L2-form-modify-controls__c262b19).
/// Shape mirrors GenerateD365Xml.generateAxFormXml (src/tools/generateD365Xml.ts) so the
/// bridge create path and the XML fallback produce the same form.
///
public static string DefaultFormClassDeclaration(string formName)
=> "[Form]\npublic class " + formName + " extends FormRun\n{\n}\n";
}
}