unit Api.Database;

interface

uses
  System.SysUtils, System.Classes, Data.DB, MemDS, DBAccess, Uni, UniProvider,
  PostgreSQLUniProvider, System.Variants, System.Generics.Collections, System.IniFiles,
  Common.Logging, Vcl.Forms, OracleUniProvider, System.Character;

type
  TApiDatabaseModule = class(TDataModule)
    ucEnvoy: TUniConnection;
    PostgreSQLUniProvider1: TPostgreSQLUniProvider;
    UniQuery1: TUniQuery;
    OracleUniProvider1: TOracleUniProvider;
    uqBooking: TUniQuery;
    uqMapUnits: TUniQuery;
    uqUnitList: TUniQuery;
    uqComplaintUnits: TUniQuery;
    uqCFSMemos: TUniQuery;
    uqComplaintList: TUniQuery;
    uqComplaintDetails: TUniQuery;
    ucENTCAD: TUniConnection;
    uqComplaintListCOMPLAINTID: TFloatField;
    uqComplaintListCFSID: TFloatField;
    uqComplaintListCOMPLAINT: TStringField;
    uqComplaintListAGENCY: TStringField;
    uqComplaintListDISPATCH_CODE_DESC: TStringField;
    uqComplaintListSOURCE: TStringField;
    uqComplaintListSOURCE_DESC: TStringField;
    uqComplaintListPRIORITY: TStringField;
    uqComplaintListADDRESSID: TFloatField;
    uqComplaintListADDRESS: TStringField;
    uqComplaintListAPARTMENT: TStringField;
    uqComplaintListCITY: TStringField;
    uqComplaintListBUSINESS: TStringField;
    uqComplaintListDISPATCHDISTRICT: TStringField;
    uqComplaintListDISPATCHSECTOR: TStringField;
    uqComplaintListADDRESSDISTRICT: TStringField;
    uqComplaintListADDRESSSECTOR: TStringField;
    uqComplaintListXCOORD: TFloatField;
    uqComplaintListYCOORD: TFloatField;
    uqComplaintListWARNINGS: TFloatField;
    uqComplaintListCONTACTS: TFloatField;
    uqComplaintListHISTORY: TFloatField;
    uqComplaintListDATEREPORTED: TDateTimeField;
    uqComplaintListDATERECEIVED: TDateTimeField;
    uqComplaintListDATEDISPATCHED: TDateTimeField;
    uqComplaintListDATERESPONDED: TDateTimeField;
    uqComplaintListDATEARRIVED: TDateTimeField;
    uqComplaintListDATECLEARED: TDateTimeField;
    uqComplaintUnitsCOMPLAINTID: TFloatField;
    uqComplaintUnitsUNITID: TFloatField;
    uqComplaintUnitsUNITNAME: TStringField;
    uqComplaintUnitsDATEDISPATCHED: TDateTimeField;
    uqComplaintUnitsDATERESPONDED: TDateTimeField;
    uqComplaintUnitsDATEARRIVED: TDateTimeField;
    uqComplaintUnitsDATECLEARED: TDateTimeField;
    uqComplaintUnitsLOCATION: TStringField;
    uqCFSMemosMEMO_ID: TFloatField;
    uqCFSMemosCFSID: TFloatField;
    uqCFSMemosMEMO_TYPE: TFloatField;
    uqCFSMemosTIMESTAMP: TDateTimeField;
    uqCFSMemosBADGE_NUMBER: TStringField;
    uqCFSMemosREMARKS: TStringField;
    uqMapUnitsENTRYID: TFloatField;
    uqMapUnitsUNITID: TFloatField;
    uqMapUnitsUNITNAME: TStringField;
    uqMapUnitsUNIT_DISTRICT: TStringField;
    uqMapUnitsGPS_LATITUDE: TFloatField;
    uqMapUnitsGPS_LONGITUDE: TFloatField;
    uqComplaintListcomplaintNumber: TStringField;
    uqComplaintListPRIORITY_COLOR: TFloatField;
    uqComplaintListDISTRICT_DESC: TStringField;
    uqComplaintListSECTOR_DESC: TStringField;
    uqUnitListUNITID: TFloatField;
    uqUnitListUNITNAME: TStringField;
    uqUnitListCARNUMBER_DESC: TStringField;
    uqUnitListDISTRICT_DESC: TStringField;
    uqUnitListSECTOR_DESC: TStringField;
    uqUnitListOFFICER1_EMPNUM: TStringField;
    uqUnitListOFFICER1_LAST_NAME: TStringField;
    uqUnitListOFFICER1_FIRST_NAME: TStringField;
    uqUnitListOFFICER1_MI: TStringField;
    uqUnitListOFFICER2_EMPNUM: TStringField;
    uqUnitListOFFICER2_LAST_NAME: TStringField;
    uqUnitListOFFICER2_FIRST_NAME: TStringField;
    uqUnitListOFFICER2_MI: TStringField;
    uqUnitListLOCATION: TStringField;
    uqUnitListCOMPLAINT: TStringField;
    uqUnitListUNITSTATUS: TFloatField;
    uqUnitListUNIT_STATUS_DESC: TStringField;
    uqUnitListENTRYID: TFloatField;
    uqUnitListGPS_LATITUDE: TFloatField;
    uqUnitListGPS_LONGITUDE: TFloatField;
    uqUnitListCALL_TYPE: TStringField;
    uqMapComplaints: TUniQuery;
    uqMapComplaintsCOMPLAINTID: TFloatField;
    uqMapComplaintsDISPATCHDISTRICT: TStringField;
    uqMapComplaintsLNG: TFloatField;
    uqMapComplaintsLAT: TFloatField;
    uqMapComplaintsDISPATCH_CODE_DESC: TStringField;
    uqMapComplaintsPRIORITY: TStringField;
    uqMapComplaintsDISPATCHCODECATEGORY: TStringField;
    uqMapComplaintspriorityKey: TStringField;
    uqMapComplaintspngName: TStringField;
    uqBadgeCounts: TUniQuery;
    uqBadgeCountsCOMPLAINTS: TFloatField;
    uqBadgeCountsUNITS: TFloatField;
    uqComplaintDetailsCOMPLAINTID: TFloatField;
    uqComplaintDetailsCFSID: TFloatField;
    uqComplaintDetailsCOMPLAINT: TStringField;
    uqComplaintDetailsPRIORITY: TStringField;
    uqComplaintDetailsDISPATCHCODE: TStringField;
    uqComplaintDetailsDISPATCH_CODE_DESC: TStringField;
    uqComplaintDetailsDISPATCHDISTRICT: TStringField;
    uqComplaintDetailsADDRESS: TStringField;
    uqComplaintDetailsDATEREPORTED: TDateTimeField;
    uqComplaintDetailsDATERECEIVED: TDateTimeField;
    uqComplaintDetailsDATEDISPATCHED: TDateTimeField;
    uqComplaintDetailsDATERESPONDED: TDateTimeField;
    uqComplaintDetailsDATEARRIVED: TDateTimeField;
    uqComplaintDetailsDATECLEARED: TDateTimeField;
    procedure DataModuleCreate(Sender: TObject);
    procedure uqComplaintListCalcFields(DataSet: TDataSet);
    procedure uqMapComplaintsCalcFields(DataSet: TDataSet);
  private
    { Private declarations }
  public
    function DerivePriorityKeyFromPriorityString(const priorityString: string): string;
    function HandleUniqueFilenames(const category: string): string;
    function BadgeCounts(const BaseQuery: TUniQuery): Integer;
    class procedure ExecSQL(const SQL: string);
  end;

var
  ApiDatabaseModule: TApiDatabaseModule;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

procedure TApiDatabaseModule.DataModuleCreate(Sender: TObject);
var
  iniFile: TIniFile;
begin
  iniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    ucEnvoy.Server := iniFile.ReadString('Database', 'Server', '');
    ucEnvoy.Database := iniFile.ReadString('Database', 'Database', '');
    ucEnvoy.Username := iniFile.ReadString('Database', 'Username', '');
    ucEnvoy.Password := iniFile.ReadString('Database', 'Password', '');

    try
      Logger.Log(2, '');
      Logger.Log(2, 'Connecting to envoyCalls Database (ApiDatabaseModule)...');
      Logger.Log(2, Format('--ucEnvoy.Server: %s ucEnvoy.Username: %s', [ucEnvoy.Server, ucEnvoy.Username]));
      if not ucEnvoy.Connected then
        ucEnvoy.Connect;
      Logger.Log(2, '--ucEnvoy connected!');
    except
      on E: Exception do
      begin
        Logger.Log(2, Format('Failed to connect to envoyCalls database: %s', [E.Message]));
      end;
    end;

    Logger.Log(1, '');
    Logger.Log(1, 'Loading Twilio settings...');

    var twilioSID := iniFile.ReadString('Twilio', 'AccountSID', '');
    if twilioSID.IsEmpty then
      Logger.Log(1, 'Twilio->AccountSID: Entry not found')
    else
      Logger.Log(1, 'Twilio->AccountSID: ' + twilioSID);

    var twilioAuth := iniFile.ReadString('Twilio', 'AuthHeader', '');
    if twilioAuth.IsEmpty then
      Logger.Log(1, 'Twilio->AuthHeader: Entry not found')
    else
      Logger.Log(1, 'Twilio->AuthHeader: ' + twilioAuth);

  finally
    iniFile.Free;
  end;
end;


class procedure TApiDatabaseModule.ExecSQL(const SQL: string);
var
  DB: TApiDatabaseModule;
begin
  DB := TApiDatabaseModule.Create(nil);
  try
    DB.UniQuery1.SQL.Text := SQL;
    DB.UniQuery1.ExecSQL;
  finally
    DB.Free;
  end;
end;

procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet);
var
  raw: string;
begin
  raw := uqComplaintListCOMPLAINT.AsString.Trim;

  if raw.Length >= 3 then
    uqComplaintListcomplaintNumber.AsString := Copy(raw, 1, 2) + '-' + Copy(raw, 3, MaxInt)
  else if raw.Length = 2 then
    uqComplaintListcomplaintNumber.AsString := raw + '-'
  else
    uqComplaintListcomplaintNumber.AsString := raw;
end;


procedure TApiDatabaseModule.uqMapComplaintsCalcFields(DataSet: TDataSet);
var
  rawCategory: string;
  rawPriority: string;
  derivedPriorityKey: string;
  computedPngName: string;
begin
  rawCategory := DataSet.FieldByName('DISPATCHCODECATEGORY').AsString;
  rawPriority := DataSet.FieldByName('PRIORITY').AsString;

  derivedPriorityKey := DerivePriorityKeyFromPriorityString(rawPriority);
  DataSet.FieldByName('priorityKey').AsString := derivedPriorityKey;

  if Trim(rawCategory) = '' then
    computedPngName := Format('default_%s.png', [derivedPriorityKey])
  else
    computedPngName := Format('%s_%s.png', [HandleUniqueFilenames(rawCategory), derivedPriorityKey]);

  DataSet.FieldByName('pngName').AsString := computedPngName;
end;

function TApiDatabaseModule.HandleUniqueFilenames(const category: string): string;
var
  i: Integer;
  ch: Char;
  lowered, resultBuilder: string;
begin
  lowered := Trim(LowerCase(category));
  resultBuilder := '';
  for i := 1 to Length(lowered) do
  begin
    ch := lowered[i];
    if ch.IsLetterOrDigit then
      resultBuilder := resultBuilder + ch
    else
      resultBuilder := resultBuilder + '_';
  end;
  Result := resultBuilder;
end;

function TApiDatabaseModule.DerivePriorityKeyFromPriorityString(const priorityString: string): string;
var
  firstChar: Char;
begin
  if priorityString <> '' then
  begin
    firstChar := priorityString[1];  // handles "3J", "3P", etc.
    if (firstChar >= '1') and (firstChar <= '4') then
      Exit(string(firstChar));
    if (firstChar >= '5') and (firstChar <= '9') then
      Exit('5-9');
  end;
  Result := '5-9';
end;


function TApiDatabaseModule.BadgeCounts(const BaseQuery: TUniQuery): Integer;
var
  Q: TUniQuery;
  WrappedSQL: string;
begin
  Q := TUniQuery.Create(nil);
  try
    Q.Connection := BaseQuery.Connection;

    // Wrap the original query text in a COUNT(*) shell
    WrappedSQL := 'select count(*) as CNT from (' + BaseQuery.SQL.Text + ') t';
    Q.SQL.Text := WrappedSQL;

    // Carry over params (if the base query uses any)
    Q.Params.AssignValues(BaseQuery.Params);

    Q.Open;
    Result := Q.Fields[0].AsInteger;
  finally
    Q.Free;
  end;
end;

end.
