unit DTOGeneratorUtils;


interface

uses
  SysUtils, Classes, Uni, UniProvider, DB, System.JSON, Pkg.Json.DTO;

 procedure GenerateDelphiJDOForTable(UniConnection: TUniConnection; const TableName: string; S: TStrings);
 function GenerateJsonDTOFromJson(const JsonStr, ClassName: string): string;
 function JsonTypeToDelphiType(const Value: TJSONValue): string;
 function SqlTypeToDelphiType(const SqlType: string): string;
 procedure GenerateJDOsForTables(UniConn: TUniConnection);
 procedure JsonToDTO(JsonStr:string);

implementation
function JsonTypeToDelphiType(const Value: TJSONValue): string;
begin
  if Value is TJSONNumber then
  begin
    if Pos('.', Value.Value) > 0 then
      Result := 'Double'
    else
      Result := 'Integer'
  end
  else if Value is TJSONString then
    Result := 'string'
  else if Value is TJSONBool then
    Result := 'Boolean'
  else if Value is TJSONArray then
    Result := 'TArray<string>' // Or further analysis could detect array of objects/numbers/etc
  else if Value is TJSONObject then
    Result := 'TObject' // Extend for nested DTOs if needed
  else if Value is TJSONNull then
    Result := 'Variant'
  else
    Result := 'string'
end;

function SqlTypeToDelphiType(const SqlType: string): string;
begin
  if SqlType.Contains('int') then
    Result := 'Integer'
  else if SqlType.Contains('char') or SqlType.Contains('text') then
    Result := 'string'
  else if SqlType.Contains('bool') then
    Result := 'Boolean'
  else if SqlType.Contains('date') or SqlType.Contains('time') then
    Result := 'TDateTime'
  else if SqlType.Contains('double') or SqlType.Contains('numeric') or SqlType.Contains('real') then
    Result := 'Double'
  else
    Result := 'string'; // fallback
end;

//utility
function PascalCase(const S: string): string;
var
  i: Integer;
  NextUpper: Boolean;
begin
  Result := '';
  NextUpper := True;
  for i := 1 to Length(S) do
  begin
    if S[i] in ['a'..'z', 'A'..'Z', '0'..'9'] then
    begin
      if NextUpper then
        Result := Result + UpCase(S[i])
      else
        Result := Result + LowerCase(S[i]);
      NextUpper := False;
    end
    else
      NextUpper := True;
  end;
end;


procedure GenerateDelphiJDOForTable(UniConnection: TUniConnection; const TableName: string; S: TStrings);
var
  Q: TUniQuery;
  FieldName, FieldType, DelphiType: string;
begin
  Q := TUniQuery.Create(nil);
  try
    Q.Connection := UniConnection;
    Q.SQL.Text := Format(
      'SELECT column_name, data_type ' +
      'FROM information_schema.columns ' +
      'WHERE table_name = %s ORDER BY ordinal_position', [QuotedStr(TableName)]);
    Q.Open;

    S.Add('type');
    S.Add('  T' + TableName + 'JDO = class');
    S.Add('  public');
    while not Q.Eof do
    begin
      FieldName := Q.FieldByName('column_name').AsString;
      FieldType := Q.FieldByName('data_type').AsString;
      DelphiType := SqlTypeToDelphiType(FieldType);
      // PascalCase the field
      FieldName := StringReplace(FieldName, '_', '', [rfReplaceAll]);
      FieldName[1] := UpCase(FieldName[1]);
      S.Add(Format('    %s: %s;', [FieldName, DelphiType]));
      Q.Next;
    end;
    S.Add('  end;');
    S.Add('');
  finally
    Q.Free;
  end;
end;


procedure GenerateJDOsForTables(UniConn: TUniConnection);
var
  TableList: TArray<string>;
  Table: string;
  OutCode: TStringList;
begin
  TableList := ['customer', 'orders', 'product']; // put your schema table names here
  OutCode := TStringList.Create;
  try


    for Table in TableList do
    begin
      OutCode.Clear();
      GenerateDelphiJDOForTable(UniConn, Table, OutCode);
      // Output to console, file, or in-memory
      OutCode.SaveToFile(Table+'JDOs.pas');
      //Writeln(OutCode.Text);
    end;


  finally
    OutCode.Free;
    UniConn.Free;
  end;
end;


function GenerateJsonDTOFromJson(const JsonStr, ClassName: string): string;
var
  Json: TJSONObject;
  JPair: TJSONPair;
  FieldType, PubPropName, PriFieldName: string;
  Code: TStringList;
begin
  Code := TStringList.Create;
  try
    Json := TJSONObject.ParseJSONValue(JsonStr) as TJSONObject;
    if not Assigned(Json) then
      raise Exception.Create('Invalid/Unsupported JSON root object.');

    Code.Add('type');
    Code.Add('  T' + PascalCase(ClassName) + 'DTO = class(TJsonDTO)');
    Code.Add('  private');
    // Private fields
    for JPair in Json do
    begin
      FieldType := JsonTypeToDelphiType(JPair.JsonValue);
      PriFieldName := 'F' + PascalCase(JPair.JsonString.Value);
      Code.Add(Format('    %s: %s;', [PriFieldName, FieldType]));
    end;
    Code.Add('  published');
    // Published properties (JSON mapping)
    for JPair in Json do
    begin
      FieldType := JsonTypeToDelphiType(JPair.JsonValue);
      PubPropName := JPair.JsonString.Value;      // Use original JSON key for property
      PriFieldName := 'F' + PascalCase(PubPropName);
      Code.Add(Format('    property %s: %s read %s write %s;', [
        PubPropName, FieldType, PriFieldName, PriFieldName
      ]));
    end;
    Code.Add('  end;');
    Result := Code.Text;
    Json.Free;
  finally
    Code.Free;
  end;
end;

procedure JsonToDTO(JsonStr:string);
var
  ClassName: string;
  ClassSource: string;
  OutCode: TStringList;
begin
  OutCode := TStringList.Create;
  try
    ClassName:='Customer';
    JsonStr := '{ "id": 1, "name": "Alice", "isActive": true, "score": 42.1, "tags": ["one", "two"] }';
    ClassSource := GenerateJsonDTOFromJson(JsonStr, ClassName);
    Writeln(ClassSource);
    OutCode.SaveToFile(ClassName+'JDOs.pas');
      //Writeln(OutCode.Text);
  finally
    OutCode.Free
  end;
end;

end.
