unit Api.ServiceImpl;

interface

uses
  XData.Server.Module, XData.Service.Common, Api.Database, Data.DB,
  System.SysUtils, System.Generics.Collections, XData.Sys.Exceptions, System.StrUtils,
  System.Hash, System.Classes, Common.Logging, System.JSON, Api.Service, VCL.Forms;


type
  [ServiceImplementation]
  TApiService = class(TInterfacedObject, IApiService)
  strict private
  ApiDB: TApiDatabaseModule;
  private
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  public
    function GetBadgeCounts: TJSONObject;
    function GetComplaintList: TJSONObject;
    function GetUnitList: TJSONObject;
    function GetComplaintMap: TJSONObject;
    function GetUnitMap: TJSONObject;
    function GetComplaintDetails(const ComplaintId: string): TJSONObject;
  end;

implementation

uses
  uLibrary;

procedure TApiService.AfterConstruction;
begin
  inherited;
  ApiDB := TApiDatabaseModule.Create(nil);
  Logger.Log(3, 'ApiDatabaseModule created');
end;

procedure TApiService.BeforeDestruction;
begin
  ApiDB.Free;
  inherited;
  Logger.Log(3, 'ApiDatabaseModule destroyed');
end;


function TApiService.GetBadgeCounts: TJSONObject;
begin
  Logger.Log(3, '---TApiService.GetBadgeCounts initiated');

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

  try
    with ApiDB.uqBadgeCounts do
    begin
      Open;
      try
        // NOTE: your TUniQuery exposes fields named COMPLAINTS and UNITS
        Result.AddPair('BadgeComplaints', TJSONNumber.Create(FieldByName('COMPLAINTS').AsInteger));
        Result.AddPair('BadgeUnits', TJSONNumber.Create(FieldByName('UNITS').AsInteger));
      finally
        Close;
      end;
    end;
  except
    on E: Exception do
    begin
      Logger.Log(3, '---TApiService.GetBadgeCounts End (error): ' + E.Message);
      raise EXDataHttpException.Create(500, 'Failed to load badge counts');
    end;
  end;

  Logger.Log(3, '---TApiService.GetBadgeCounts End');
end;

function TApiService.GetComplaintMap: TJSONObject;
var
  data: TJSONArray;
  emitted: Integer;
  item: TJSONObject;
begin
  Logger.Log(3, '---TApiService.GetComplaintMap initiated');

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

  data := TJSONArray.Create;
  try
    emitted := 0;
    with ApiDB.uqMapComplaints do
    begin
      Open;
      First;
      while not Eof do
      begin
        if ApiDB.uqMapComplaintsLAT.IsNull or ApiDB.uqMapComplaintsLNG.IsNull then
        begin
          Next;
          Continue;
        end;

        item := TJSONObject.Create;

        item.AddPair('ComplaintId', ApiDB.uqMapComplaintsCOMPLAINTID.AsString);
        item.AddPair('DispatchDistrict', ApiDB.uqMapComplaintsDISPATCHDISTRICT.AsString);
        item.AddPair('DispatchCodeDesc', ApiDB.uqMapComplaintsDISPATCH_CODE_DESC.AsString);

        item.AddPair('DispatchCodeCategory', ApiDB.uqMapComplaintsDISPATCHCODECATEGORY.AsString);
        item.AddPair('Priority', ApiDB.uqMapComplaintsPRIORITY.AsString);
        item.AddPair('priorityKey', ApiDB.uqMapComplaints.FieldByName('priorityKey').AsString);
        item.AddPair('pngName', ApiDB.uqMapComplaints.FieldByName('pngName').AsString);

        item.AddPair('Lat', TJSONNumber.Create(ApiDB.uqMapComplaintsLAT.AsFloat));
        item.AddPair('Lng', TJSONNumber.Create(ApiDB.uqMapComplaintsLNG.AsFloat));

        data.AddElement(item);
        Inc(emitted);
        Next;
      end;
    end;

    Result.AddPair('count', TJSONNumber.Create(data.Count));
    Result.AddPair('returned', TJSONNumber.Create(emitted));
    Result.AddPair('data', data);
  except
    data.Free;
    Logger.Log(3, '---TApiService.GetComplaintMap End (error)');
    raise EXDataHttpException.Create(500, 'Failed to load complaint map');
  end;

  Logger.Log(3, '---TApiService.GetComplaintMap End');
end;


function TApiService.GetUnitMap: TJSONObject;
var
  data: TJSONArray;
begin
  Logger.Log(3, '---TApiService.GetUnitMap initiated');

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

  data := TJSONArray.Create;
  try
    with ApiDB.uqMapUnits do
    begin
      Open;
      First;
      while not Eof do
      begin
        // skip rows without coordinates
        if (not FieldByName('GPS_LATITUDE').IsNull) and (not FieldByName('GPS_LONGITUDE').IsNull) then
        begin
          var item := TJSONObject.Create;
          item.AddPair('UnitId', ApiDB.uqMapUnitsUNITID.AsString);
          item.AddPair('UnitName', ApiDB.uqMapUnitsUNITNAME.AsString);
          item.AddPair('District', ApiDB.uqMapUnitsUNIT_DISTRICT.AsString);
          item.AddPair('Lat', TJSONNumber.Create(ApiDB.uqMapUnitsGPS_LATITUDE.AsFloat));
          item.AddPair('Lng', TJSONNumber.Create(ApiDB.uqMapUnitsGPS_LONGITUDE.AsFloat));
          data.AddElement(item);
        end;
        Next;
      end;
    end;

    Result.AddPair('count', TJSONNumber.Create(data.Count));
    Result.AddPair('returned', TJSONNumber.Create(data.Count));
    Result.AddPair('data', data);
  except
    data.Free;
    Logger.Log(3, '---TApiService.GetUnitMap End (error)');
    raise EXDataHttpException.Create(500, 'Failed to load unit map');
  end;

  Logger.Log(3, '---TApiService.GetUnitMap End');
end;


function TApiService.GetComplaintList: TJSONObject;
var
  data: TJSONArray;
  lastDistrict: string;
begin
  Logger.Log(3, '---TApiService.GetComplaintList initiated');

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

  data := TJSONArray.Create;
  try
    lastDistrict := '';
    with ApiDB.uqComplaintList do
    begin
      Open;
      (FieldByName('DATEREPORTED') as TDateTimeField).DisplayFormat := 'yyyy-mm-dd hh:nn:ss';
      First;
      while not Eof do
      begin
        var status: string;
        if not FieldByName('DATECLEARED').IsNull then
          status := 'Cleared'
        else if not FieldByName('DATEARRIVED').IsNull then
          status := 'AtScene'
        else if not FieldByName('DATEDISPATCHED').IsNull then
          status := 'Dispatched'
        else
          status := 'Pending';

        var item := TJSONObject.Create;

        // Add a section header only when the district changes
        var curDistrict := ApiDB.uqComplaintListDISPATCHDISTRICT.AsString;
        if not SameText(curDistrict, lastDistrict) then
          item.AddPair('DistrictHeader', curDistrict);
        lastDistrict := curDistrict;

        var districtSector := ApiDB.uqComplaintListDISTRICT_DESC.AsString + ApiDB.uqComplaintListSECTOR_DESC.AsString;
        item.AddPair('DistrictSector', districtSector);

        // existing color hex
        var colorVal := ApiDB.uqComplaintListPRIORITY_COLOR.AsInteger;
        item.AddPair('PriorityColor', '#' + IntToHex(colorVal and $FFFFFF, 6));

        // Text is white only for the deep blue (255 = $0000FF), black otherwise
        if (colorVal and $FFFFFF) = $0000FF then
          item.AddPair('PriorityTextColor', '#FFFFFF')
        else
          item.AddPair('PriorityTextColor', '#000000');

        item.AddPair('ComplaintId', ApiDB.uqComplaintListCOMPLAINTID.AsString);
        item.AddPair('Complaint', ApiDB.uqComplaintListcomplaintNumber.AsString);
        item.AddPair('Agency', ApiDB.uqComplaintListAGENCY.AsString);
        item.AddPair('Priority', ApiDB.uqComplaintListPRIORITY.AsString);
        item.AddPair('DispatchCodeDesc', ApiDB.uqComplaintListDISPATCH_CODE_DESC.AsString);
        item.AddPair('Address', ApiDB.uqComplaintListADDRESS.AsString);
        item.AddPair('CFSId', ApiDB.uqComplaintListCFSID.AsString);
        item.AddPair('Status', status);
        item.AddPair('DispatchDistrict', curDistrict);
        item.AddPair('DateReported', ApiDB.uqComplaintListDATEREPORTED.AsString);

        data.AddElement(item);
        Next;
      end;
    end;

    Result.AddPair('count', TJSONNumber.Create(data.Count));
    Result.AddPair('returned', TJSONNumber.Create(data.Count));
    Result.AddPair('data', data);
  except
    data.Free;
    Logger.Log(3, '---TApiService.GetComplaintList End (error)');
    raise EXDataHttpException.Create(500, 'Failed to load complaints list');
  end;

  Logger.Log(3, '---TApiService.GetComplaintList End');
end;


function TApiService.GetUnitList: TJSONObject;
var
  data: TJSONArray;
  lastDistrict: string;
begin
  Logger.Log(3, '---TApiService.GetUnitList initiated');

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

  data := TJSONArray.Create;
  try
    lastDistrict := '';
    with ApiDB.uqUnitList do
    begin
      Open;
      First;
      while not Eof do
      begin
        var item := TJSONObject.Create;


        // Group header: show once when district changes
        var curDistrict := ApiDB.uqUnitListDISTRICT_DESC.AsString;
        var header := IfThen(curDistrict <> '', curDistrict + ' District', '');
        if (header <> '') and not SameText(header, lastDistrict) then
          item.AddPair('DistrictHeader', header);
        lastDistrict := header;


        // Core unit identity
        item.AddPair('UnitId', ApiDB.uqUnitListUNITID.AsString);
        item.AddPair('UnitName', ApiDB.uqUnitListUNITNAME.AsString);
        item.AddPair('CarNumberDesc', ApiDB.uqUnitListCARNUMBER_DESC.AsString);
        item.AddPair('District', curDistrict);
        item.AddPair('Sector', ApiDB.uqUnitListSECTOR_DESC.AsString);
        item.AddPair('CallType', ApiDB.uqUnitListCALL_TYPE.AsString);


        // Current assignment (if any)
        item.AddPair('Location', ApiDB.uqUnitListLOCATION.AsString);
        item.AddPair('Complaint', ApiDB.uqUnitListCOMPLAINT.AsString);

        // Status: default to "Available" when no active CFS row
       var statusDesc := ApiDB.uqUnitListUNIT_STATUS_DESC.AsString;
       if statusDesc = '' then
         statusDesc := 'Available';
       item.AddPair('Status', statusDesc);

        // Officers (LAST, FIRST [MI])
        var o1 := Trim(ApiDB.uqUnitListOFFICER1_LAST_NAME.AsString);
        var f1 := Trim(ApiDB.uqUnitListOFFICER1_FIRST_NAME.AsString);
        var m1 := Trim(ApiDB.uqUnitListOFFICER1_MI.AsString);
        if o1 <> '' then
        begin
          if f1 <> '' then o1 := o1 + ', ' + f1;
          if m1 <> '' then o1 := o1 + ' ' + m1;
          item.AddPair('Officer1', o1);
        end;

        var o2 := Trim(ApiDB.uqUnitListOFFICER2_LAST_NAME.AsString);
        var f2 := Trim(ApiDB.uqUnitListOFFICER2_FIRST_NAME.AsString);
        var m2 := Trim(ApiDB.uqUnitListOFFICER2_MI.AsString);
        if o2 <> '' then
        begin
          if f2 <> '' then o2 := o2 + ', ' + f2;
          if m2 <> '' then o2 := o2 + ' ' + m2;
          item.AddPair('Officer2', o2);
        end;

        data.AddElement(item);
        Next;
      end;
    end;

    Result.AddPair('count', TJSONNumber.Create(data.Count));
    Result.AddPair('returned', TJSONNumber.Create(data.Count));
    Result.AddPair('data', data);
  except
    data.Free;
    Logger.Log(3, '---TApiService.GetUnitList End (error)');
    raise EXDataHttpException.Create(500, 'Failed to load units list');
  end;

  Logger.Log(3, '---TApiService.GetUnitList End');
end;



function TApiService.GetComplaintDetails(const ComplaintId: string): TJSONObject;
var
  obj: TJSONObject;
begin
  Logger.Log(3,'---TApiService.GetComplaintDetails initiated: '+ComplaintId);
  Result := TJSONObject.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
  try
    with ApiDB.uqComplaintDetails do
    begin
      ParamByName('COMPLAINTID').AsString := ComplaintId;
      Open;
      try
        if Eof then raise EXDataHttpException.Create(404,'Complaint not found');

        obj := TJSONObject.Create;
        obj.AddPair('ComplaintId',FieldByName('COMPLAINTID').AsString);
        obj.AddPair('CFSId',FieldByName('CFSID').AsString);
        obj.AddPair('Complaint',FieldByName('COMPLAINT').AsString);
        obj.AddPair('Priority',FieldByName('PRIORITY').AsString);
        obj.AddPair('DispatchCode',FieldByName('DISPATCHCODE').AsString);
        obj.AddPair('DispatchCodeDesc',FieldByName('DISPATCH_CODE_DESC').AsString);
        obj.AddPair('DispatchDistrict',FieldByName('DISPATCHDISTRICT').AsString);
        obj.AddPair('Address',FieldByName('ADDRESS').AsString);

        if FieldByName('DATEREPORTED').IsNull
          then obj.AddPair('DateReported','')
          else obj.AddPair('DateReported',FormatDateTime('yyyy-mm-dd hh:nn:ss',(FieldByName('DATEREPORTED') as TDateTimeField).AsDateTime));
        if FieldByName('DATERECEIVED').IsNull
          then obj.AddPair('DateReceived','')
          else obj.AddPair('DateReceived',FormatDateTime('yyyy-mm-dd hh:nn:ss',(FieldByName('DATERECEIVED') as TDateTimeField).AsDateTime));
        if FieldByName('DATEDISPATCHED').IsNull
          then obj.AddPair('DateDispatched','')
          else obj.AddPair('DateDispatched',FormatDateTime('yyyy-mm-dd hh:nn:ss',(FieldByName('DATEDISPATCHED') as TDateTimeField).AsDateTime));
        if FieldByName('DATERESPONDED').IsNull
          then obj.AddPair('DateResponded','')
          else obj.AddPair('DateResponded',FormatDateTime('yyyy-mm-dd hh:nn:ss',(FieldByName('DATERESPONDED') as TDateTimeField).AsDateTime));
        if FieldByName('DATEARRIVED').IsNull
          then obj.AddPair('DateArrived','')
          else obj.AddPair('DateArrived',FormatDateTime('yyyy-mm-dd hh:nn:ss',(FieldByName('DATEARRIVED') as TDateTimeField).AsDateTime));
        if FieldByName('DATECLEARED').IsNull
          then obj.AddPair('DateCleared','')
          else obj.AddPair('DateCleared',FormatDateTime('yyyy-mm-dd hh:nn:ss',(FieldByName('DATECLEARED') as TDateTimeField).AsDateTime));

        Result.AddPair('data',obj);
      finally
        Close;
      end;
    end;
  except
    on E: EXDataHttpException do
    begin
      Logger.Log(3,'---TApiService.GetComplaintDetails not found');
      raise;
    end;
    on E: Exception do
    begin
      Logger.Log(3,'---TApiService.GetComplaintDetails error: '+E.Message);
      raise EXDataHttpException.Create(500,'Failed to load complaint details');
    end;
  end;
  Logger.Log(3,'---TApiService.GetComplaintDetails End');
end;




initialization
  RegisterServiceType(TApiService);

end.

