unit Api.ServiceImpl;

interface

uses
  XData.Server.Module,
  XData.Service.Common,
  System.Variants,
  System.DateUtils,
  Api.Service,
  Api.Database,
  Common.Logging,
  System.SysUtils, JSON, System.IniFiles;

type
  [ServiceImplementation]
  TApiService = class(TInterfacedObject, IApiService)
  strict private
    apiDB: TApiDatabase;
  private
    procedure EnsureBlankWebTaskRow(const taskId: string);
    function BuildTaskNumber: string;
    function BuildTaskTitle(const taskNumber, projectName, subject: string): string;
    function SaveTaskRow(Item: TTaskRowSave): Boolean;
    function TestApi(messageText: string): TJSONObject;
    function GetWebClientVersion: string;
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    function GetTaskItems(taskId: string): TTaskItemsResponse;
  end;

implementation

procedure TApiService.AfterConstruction;
begin
  inherited;
  apiDB := TApiDatabase.Create(nil);
  Logger.Log(4, 'ApiService.AfterConstruction - apiDB created');
end;

procedure TApiService.BeforeDestruction;
begin
  Logger.Log(4, 'ApiService.BeforeDestruction - freeing apiDB');
  apiDB.Free;
  inherited;
end;

function TApiService.GetTaskItems(taskId: string): TTaskItemsResponse;
var
  taskHeader: TTaskHeader;
  item: TTaskItem;
  useParentView: Boolean;
begin
  Logger.Log(4, Format('ApiService.GetTaskItems - TASK_ID="%s"', [taskId]));

  Result := TTaskItemsResponse.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);

  try
    apiDB.uqTaskHeader.Close;
    apiDB.uqTaskHeader.ParamByName('TASK_ID').AsString := taskId;
    apiDB.uqTaskHeader.Open;

    if apiDB.uqTaskHeader.IsEmpty then
    begin
      Logger.Log(2, Format('ApiService.GetTaskItems - no task header found for TASK_ID="%s"', [taskId]));
      Result.count := 0;
      Exit;
    end;

    taskHeader := TTaskHeader.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add(taskHeader);

    taskHeader.taskId := apiDB.uqTaskHeaderTASK_ID.AsString;
    taskHeader.parentId := apiDB.uqTaskHeaderPARENT_ID.AsString;
    taskHeader.taskNumber := BuildTaskNumber;
    taskHeader.projectName := apiDB.uqTaskHeaderPROJECT_NAME.AsString;
    taskHeader.subject := apiDB.uqTaskHeaderSUBJECT.AsString;
    taskHeader.title := BuildTaskTitle(
      taskHeader.taskNumber,
      taskHeader.projectName,
      taskHeader.subject
    );

    Result.task := taskHeader;

    useParentView := Trim(Result.task.parentId) = '';
    useParentView := useParentView or (Trim(Result.task.parentId) = '0');

    if useParentView then
    begin
      apiDB.uqTaskItemsForParent.Close;
      apiDB.uqTaskItemsForParent.ParamByName('TASK_ID').AsString := taskId;
      apiDB.uqTaskItemsForParent.Open;

      if apiDB.uqTaskItemsForParent.IsEmpty then
      begin
        Logger.Log(4, Format('ApiService.GetTaskItems - no rows for TASK_ID="%s", ensuring blank row', [taskId]));
        EnsureBlankWebTaskRow(taskId);

        apiDB.uqTaskItemsForParent.Close;
        apiDB.uqTaskItemsForParent.ParamByName('TASK_ID').AsString := taskId;
        apiDB.uqTaskItemsForParent.Open;
      end;

      while not apiDB.uqTaskItemsForParent.Eof do
      begin
        item := TTaskItem.Create;
        TXDataOperationContext.Current.Handler.ManagedObjects.Add(item);

        item.taskItemId := apiDB.uqTaskItemsForParentTASK_ITEM_ID.AsString;
        item.taskId := apiDB.uqTaskItemsForParentTASK_ID.AsString;
        item.itemNum := apiDB.uqTaskItemsForParentITEM_NUM.AsInteger;

        item.application := apiDB.uqTaskItemsForParentAPPLICATION.AsString;
        item.version := apiDB.uqTaskItemsForParentAPP_VERSION.AsString;

        if apiDB.uqTaskItemsForParentTASK_DATE.IsNull then
          item.taskDate := 0
        else
          item.taskDate := apiDB.uqTaskItemsForParentTASK_DATE.AsDateTime;

        item.reportedBy := apiDB.uqTaskItemsForParentREPORTED_BY.AsString;
        item.assignedTo := apiDB.uqTaskItemsForParentASSIGNED_TO.AsString;
        item.status := apiDB.uqTaskItemsForParentSTATUS.AsString;

        if apiDB.uqTaskItemsForParentSTATUS_DATE.IsNull then
          item.statusDate := Null
        else
          item.statusDate := apiDB.uqTaskItemsForParentSTATUS_DATE.AsDateTime;

        item.fixedVersion := apiDB.uqTaskItemsForParentFIXED_VERSION.AsString;
        item.formSection := apiDB.uqTaskItemsForParentFORM_SECTION.AsString;
        item.issue := apiDB.uqTaskItemsForParentISSUE.AsString;
        item.notes := apiDB.uqTaskItemsForParentNOTES.AsString;

        Result.items.Add(item);

        apiDB.uqTaskItemsForParent.Next;
      end;
    end
    else
    begin
      apiDB.uqTaskItems.Close;
      apiDB.uqTaskItems.ParamByName('TASK_ID').AsString := taskId;
      apiDB.uqTaskItems.Open;

      if apiDB.uqTaskItems.IsEmpty then
      begin
        Logger.Log(4, Format('ApiService.GetTaskItems - no rows for TASK_ID="%s", ensuring blank row', [taskId]));
        EnsureBlankWebTaskRow(taskId);

        apiDB.uqTaskItems.Close;
        apiDB.uqTaskItems.ParamByName('TASK_ID').AsString := taskId;
        apiDB.uqTaskItems.Open;
      end;

      while not apiDB.uqTaskItems.Eof do
      begin
        item := TTaskItem.Create;
        TXDataOperationContext.Current.Handler.ManagedObjects.Add(item);

        item.taskItemId := apiDB.uqTaskItemsTASK_ITEM_ID.AsString;
        item.taskId := apiDB.uqTaskItemsTASK_ID.AsString;
        item.itemNum := apiDB.uqTaskItemsITEM_NUM.AsInteger;

        item.application := apiDB.uqTaskItemsAPPLICATION.AsString;
        item.version := apiDB.uqTaskItemsAPP_VERSION.AsString;

        if apiDB.uqTaskItemsTASK_DATE.IsNull then
          item.taskDate := 0
        else
          item.taskDate := apiDB.uqTaskItemsTASK_DATE.AsDateTime;

        item.reportedBy := apiDB.uqTaskItemsREPORTED_BY.AsString;
        item.assignedTo := apiDB.uqTaskItemsASSIGNED_TO.AsString;
        item.status := apiDB.uqTaskItemsSTATUS.AsString;

        if apiDB.uqTaskItemsSTATUS_DATE.IsNull then
          item.statusDate := Null
        else
          item.statusDate := apiDB.uqTaskItemsSTATUS_DATE.AsDateTime;

        item.fixedVersion := apiDB.uqTaskItemsFIXED_VERSION.AsString;
        item.formSection := apiDB.uqTaskItemsFORM_SECTION.AsString;
        item.issue := apiDB.uqTaskItemsISSUE.AsString;
        item.notes := apiDB.uqTaskItemsNOTES.AsString;

        Result.items.Add(item);

        apiDB.uqTaskItems.Next;
      end;
    end;

    Result.count := Result.items.Count;
    Logger.Log(4, Format('ApiService.GetTaskItems - returned %d item(s)', [Result.count]));
  except
    on E: Exception do
    begin
      Logger.Log(2, 'ApiService.GetTaskItems - ERROR: ' + E.Message);
      raise;
    end;
  end;
end;


procedure TApiService.EnsureBlankWebTaskRow(const taskId: string);
begin
  Logger.Log(4, Format('ApiService.EnsureBlankWebTaskRow - TASK_ID="%s"', [taskId]));
  try
    apiDB.uqEnsureBlankRow.Close;
    apiDB.uqEnsureBlankRow.ParamByName('TASK_ID').AsString := taskId;
    apiDB.uqEnsureBlankRow.ExecSQL;
  except
    on E: Exception do
    begin
      Logger.Log(2, 'ApiService.EnsureBlankWebTaskRow - ERROR: ' + E.Message);
      raise;
    end;
  end;
end;


function TApiService.BuildTaskNumber: string;

  procedure AddPart(const value: string);
  var
    s: string;
  begin
    s := Trim(value);
    if s = '' then
      Exit;

    if Result = '' then
      Result := s
    else
      Result := Result + '.' + s;
  end;

begin
  Result := '';

  AddPart(apiDB.uqTaskHeaderTASK_NUM_1.AsString);
  AddPart(apiDB.uqTaskHeaderTASK_NUM_2.AsString);
  AddPart(apiDB.uqTaskHeaderTASK_NUM_3.AsString);
  AddPart(apiDB.uqTaskHeaderTASK_NUM_4.AsString);
  AddPart(apiDB.uqTaskHeaderTASK_NUM_5.AsString);
  AddPart(apiDB.uqTaskHeaderTASK_NUM_6.AsString);
end;


function TApiService.BuildTaskTitle(const taskNumber, projectName, subject: string): string;
begin
  Result := 'Task';

  if Trim(taskNumber) <> '' then
    Result := Result + ' - ' + Trim(taskNumber);

  if Trim(projectName) <> '' then
    Result := Result + ' | ' + Trim(projectName);

  if Trim(subject) <> '' then
    Result := Result + ' | ' + Trim(subject);
end;


function TApiService.SaveTaskRow(Item: TTaskRowSave): Boolean;

  function ParseDateOrZero(const S: string; out D: TDateTime): Boolean;
  begin
    Result := Trim(S) <> '';
    if Result then
      D := ISO8601ToDate(S, False);
  end;

var
  d: TDateTime;
begin
  Logger.Log(4, Format('ApiService.SaveTaskRow - TASK_ITEM_ID="%s"', [Item.taskItemId]));

  try
    apiDB.uqSaveTaskRow.Close;

    apiDB.uqSaveTaskRow.ParamByName('TASK_ITEM_ID').AsString := Item.taskItemId;

    apiDB.uqSaveTaskRow.ParamByName('APPLICATION').AsString := Item.application;
    apiDB.uqSaveTaskRow.ParamByName('APP_VERSION').AsString := Item.version;

    if ParseDateOrZero(Item.taskDate, d) then
      apiDB.uqSaveTaskRow.ParamByName('TASK_DATE').AsDateTime := d
    else
      apiDB.uqSaveTaskRow.ParamByName('TASK_DATE').Clear;

    apiDB.uqSaveTaskRow.ParamByName('REPORTED_BY').AsString := Item.reportedBy;
    apiDB.uqSaveTaskRow.ParamByName('ASSIGNED_TO').AsString := Item.assignedTo;

    apiDB.uqSaveTaskRow.ParamByName('STATUS').AsString := Item.status;

    if ParseDateOrZero(Item.statusDate, d) then
      apiDB.uqSaveTaskRow.ParamByName('STATUS_DATE').AsDateTime := d
    else
      apiDB.uqSaveTaskRow.ParamByName('STATUS_DATE').AsDateTime := Date;

    apiDB.uqSaveTaskRow.ParamByName('FIXED_VERSION').AsString := Item.fixedVersion;
    apiDB.uqSaveTaskRow.ParamByName('FORM_SECTION').AsString := Item.formSection;
    apiDB.uqSaveTaskRow.ParamByName('ISSUE').AsString := Item.issue;
    apiDB.uqSaveTaskRow.ParamByName('NOTES').AsString := Item.notes;

    apiDB.uqSaveTaskRow.ExecSQL;

    Result := True;
    Logger.Log(4, 'ApiService.SaveTaskRow - OK');
  except
    on E: Exception do
    begin
      Logger.Log(2, 'ApiService.SaveTaskRow - ERROR: ' + E.Message);
      raise;
    end;
  end;
end;


function TApiService.TestApi(messageText: string): TJSONObject;
var
  requiredVersion: string;
begin
  Logger.Log(3, 'IApiService.TestApi called');

  Result := TJSONObject.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);

  requiredVersion := GetWebClientVersion;

  Result.AddPair('messageEcho', messageText);
  Result.AddPair('serverTime', DateTimeToStr(Now));
  Result.AddPair('requiredWebClientVersion', requiredVersion);
  Result.AddPair('note', 'If this endpoint is reachable, JWT auth passed. Version enforcement on every API call is a separate step (middleware).');
end;


function TApiService.GetWebClientVersion: string;
var
  iniFile: TIniFile;
begin
  iniFile := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  try
    Result := iniFile.ReadString('Settings', 'webClientVersion', '');
  finally
    iniFile.Free;
  end;
end;

initialization
  RegisterServiceType(TypeInfo(IApiService));
  RegisterServiceType(TApiService);

end.

