#define AppName "Centreon Monitoring Agent"
#define AppVersion "@AGENT_VERSION@"
#define AppPublisher "Centreon"
#define AppURL "https://www.centreon.com/"
#define AppExeName "centagent.exe"


#define CentreonIcon "resources\centreon_icon.ico"
#define CentreonLogo "resources\centreon_logo.bmp"
#define CentreonLogoLarge "resources\centreon_logo_large.bmp"
#define WarningIcon "resources\warning_icon.bmp"
#define InfoIcon "resources\info_icon.bmp"

#define OutputDir "@OUTPUT_DIR@"
#define OutputBaseFilename "@OUTPUT_BASE_FILENAME_MODIFIER@"


[Setup]
AppName={#AppName}
AppVersion={#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
DefaultDirName={autopf}\Centreon
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
OutputBaseFilename={#OutputBaseFilename}
OutputDir={#OutputDir}
WizardStyle=modern
WizardResizable=no
DisableWelcomePage=yes
DisableDirPage=yes
SetupIconFile ={#CentreonIcon}
PrivilegesRequired=admin
Uninstallable=no
DisableStartupPrompt=yes
WizardImageFile={#CentreonLogoLarge}
WizardSmallImageFile={#CentreonLogo}
WizardImageStretch=yes


[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
Source: {#InfoIcon};   Flags: dontcopy
Source: {#WarningIcon}; Flags: dontcopy

[Code]
{ ────────────────────────────────────────────────────────────────────── }
{ ──────────────────────────── DLL ───────────────────────────────────── }
{ ────────────────────────────────────────────────────────────────────── }

function GetComputerName(lpBuffer: String; var nSize: DWORD): Boolean;
  external 'GetComputerNameW@kernel32.dll stdcall';

function InternetCheckConnectionW(lpszUrl: string; dwFlags: DWORD; dwReserved: DWORD): BOOL;
  external 'InternetCheckConnectionW@wininet.dll stdcall';

procedure ExitProcess(uExitCode: UINT);
  external 'ExitProcess@kernel32.dll stdcall';

{ ────────────────────────────────────────────────────────────────────── }
{ ───────────────────── Global Variables and Wizard ──────────────────── }
{ ────────────────────────────────────────────────────────────────────── }
var
// ─────────────────────────────────────────────
// Pages:
// ─────────────────────────────────────────────
  ConfigPage: TWizardPage;
  EncryptionPage: TWizardPage;
// ─────────────────────────────────────────────
// Widget for config pages
// ─────────────────────────────────────────────

  AgentInstanceLabel: TNewStaticText;
  AgentInstanceComboBox: TNewComboBox;

  HostLabel, HostTemplateLabel, PollerLabel: TNewStaticText;
  HostNameEdit, HostTemplateEdit, PollerEndpointEdit: TNewEdit;
  PollerInitiatedCheckBox: TNewCheckBox;
  HostInfoBitmapImage, HostTemplateInfoBitmapImage, PollerInfoBitmapImage, PollerInitiatedInfoBitmapImage:TBitmapImage;
  HostWarningBitmapImage, PollerWarningBitmapImage:TBitmapImage;
  
  LogTypeLabel, LogLeveLabel, LogFileLabel, MaxFileLabel, MaxNumberLabel, CustomCheckLabel: TNewStaticText;
  LogFileEdit, MaxFileEdit, MaxNumberEdit, CustomCheckEdit: TNewEdit;
  MaxFileInfoBitmapImage, MaxNumberInfoBitmapImage:TBitmapImage;
  LogTypeComboBox, LogLeveLComboBox:TNewComboBox;
  LogFileBrowseButton, CustomCheckBrowseButton : TButton;
  LogFileWarningBitmapImage:TBitmapImage;
// ─────────────────────────────────────────────
// Widget for Encryption
// ─────────────────────────────────────────────
  EncryptionLabel: TNewStaticText;
  EncryptionComboBox: TNewComboBox;
  PublicCertLabel, PrivateKeyLabel, CaLabel, CommonNameLabel, TokenLabel: TNewStaticText;
  PublicCertBrowseButton, PrivateKeyBrowseButton, CABrowseButton : TButton;
  PublicCertEdit, PrivateKeyEdit, CaEdit, CommonNameEdit, TokenEdit: TNewEdit;
  PublicCertInfoBitmapImage, PrivateKeyInfoBitmapImage, CaInfoBitmapImage, CommonNameInfoBitmapImage, TokenInfoBitmapImage:TBitmapImage;
  PublicCertWarningBitmapImage, PrivateKeyWarningBitmapImage, TokenWarningBitmapImage,CaWarningBitmapImage:TBitmapImage;
  
  AgentNames: array of string;
{ ────────────────────────────────────────────────────────────────────── }
{ ─────────────────────── Event Handlers ─────────────────────────────── }
{ ────────────────────────────────────────────────────────────────────── }
//Handles clicks on info bitmap images and displays a context-specific MsgBox for each known control.
//Compares Sender to known image components and shows the appropriate explanatory message.
procedure BitmapImageOnClick(Sender: TObject);
begin
  if Sender = HostInfoBitmapImage then
    MsgBox('The name of the host as defined in the Centreon interface.', mbInformation, MB_OK)
  else if Sender = HostTemplateInfoBitmapImage then
    MsgBox('The host template applied to the host in Centreon.', mbInformation, MB_OK)
  else if Sender = PollerInfoBitmapImage then begin
    if PollerInitiatedCheckBox.Checked then
      MsgBox('Interface and port on which the agent will accept connections from the poller. 0.0.0.0 means all interfaces.', mbInformation, MB_OK)
    else
      MsgBox('IP address of DNS name of the poller the agent will connect to.', mbInformation, MB_OK);
    end 
  else if Sender = PollerInitiatedInfoBitmapImage then
    MsgBox('Use when the agent cannot connect to the poller directly: the poller will initiate the connection.', mbInformation, MB_OK)
  else if Sender = MaxFileInfoBitmapImage then
    MsgBox('For the rotation of logs to be active, it is necessary that both parameters ''Max File Size'' and ''Max number of files'' are set. The space used by the logs of the agent will not exceed ''Max File Size'' * ''Max number of files''.', mbInformation, MB_OK)
  else if Sender = MaxNumberInfoBitmapImage then
    MsgBox('For the rotation of logs to be active, it is necessary that both parameters ''Max File Size'' and ''Max number of files'' are set. The space used by the logs of the agent will not exceed ''Max File Size'' * ''Max number of files''.', mbInformation, MB_OK)
  else if Sender = PublicCertInfoBitmapImage then
    MsgBox('Public certificate file path. Mandatory if encryption and poller-initiated connection are active.', mbInformation, MB_OK)
  else if Sender = PrivateKeyInfoBitmapImage then
    MsgBox('Private key file path. Mandatory if encryption and poller-initiated connection are active.', mbInformation, MB_OK)
  else if Sender = CaInfoBitmapImage then
    MsgBox('Trusted CA''s certificate file.', mbInformation, MB_OK)
  else if Sender = CommonNameInfoBitmapImage then
    MsgBox('Expected TLS certificate common name (CN) - leave blank if unsure.', mbInformation, MB_OK)
  else if Sender = TokenInfoBitmapImage then
    MsgBox('Expected JWT(Json Web Token)', mbInformation, MB_OK)
  else
    MsgBox('Unknown image clicked!', mbInformation, MB_OK);
end;
 
// Updates UI when PollerInitiatedCheckBox is toggled: sets PollerLabel to
// "Listening interface:" when checked or "Poller endpoint:" when unchecked, and promotes "Insecure TLS" to "TLS" if enabled.
procedure OnPollerInitiatedToggle(Sender: TObject);
begin
  if PollerInitiatedCheckBox.Checked then
  begin
    PollerLabel.Caption := 'Listening interface:';
    if EncryptionComboBox.Text = 'Insecure TLS' then
      EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('TLS');
  end
  else
    PollerLabel.Caption := 'Poller endpoint:';
end;

// Updates UI when the log type changes: shows file-path, max-file, and max-number controls when "File" is selected, otherwise hides them.
procedure OnLogTypeChange(Sender: TObject);
begin
  if LogTypeComboBox.Text = 'File'  then
  begin
    LogFileLabel.Show;
    LogFileEdit.Show;
    LogFileBrowseButton.Show;
    
    MaxFileLabel.Show;
    MaxFileEdit.Show;
    MaxFileInfoBitmapImage.Show;
    
    MaxNumberLabel.Show;
    MaxNumberEdit.Show;
    MaxNumberInfoBitmapImage.Show;
    end
  else
  begin
    LogFileLabel.Hide;
    LogFileEdit.Hide;
    LogFileBrowseButton.Hide;
    LogFileWarningBitmapImage.Hide;
    
    MaxFileLabel.Hide;
    MaxFileEdit.Hide;
    MaxFileInfoBitmapImage.Hide;
    
    MaxNumberLabel.Hide;
    MaxNumberEdit.Hide;
    MaxNumberInfoBitmapImage.Hide;
 
  end;
end;

// Opens a Save File dialog to select a log or text file and sets LogFileEdit.Text to the chosen path.
// Sender is the event source (not used by this handler).
procedure LogFileBrowseFile(Sender: TObject);
var
  Path : String;
begin
  if(GetSaveFileName('Select a file', Path, '','Log Files (*.log)|*.log|Text Documents (*.txt)|*.txt|All Files|*.*','txt')) then
  begin
     LogFileEdit.Text:= Path;
  end;
end;

// Opens a file picker for selecting 
procedure CheckFileBrowseFile(Sender: TObject);
var
  Path : String;
begin
  if(GetOpenFileName('Select custom check file', Path, '','All Files (*.*)|*.*|INI Files (*.ini)|*.ini|TXT Files (*.txt)|*.txt', 'txt')) then
  begin
     CustomCheckEdit.Text:= Path;
  end;
end;

//Allows only digits and backspace in the input; shows an error message for invalid characters and cancels the keystroke.
procedure NumbersOnlyKeyPress(Sender: TObject; var Key: Char);
begin
  if Key <> #8 then 
    if Pos(Key, '0123456789') = 0 then  // #8 = backspace
    begin
      SuppressibleMsgBox('Invalid character'#13#10'Please enter numbers only. Letters and special characters are not allowed in this field.', mbError, MB_OK, IDOK);
      Key := #0;  // cancel the key
    end;
end;

// UI controls based on the selected encryption mode and PollerInitiatedCheckBox.
// Shows cert/key/token for TLS when poller-initiated; otherwise shows CA (and CommonName for "Insecure TLS") plus token.
procedure OnEncryptionChange(Sender: TObject);
begin
  // Hide all encryption-related fields first
  PublicCertLabel.Hide;
  PublicCertEdit.Hide;
  PublicCertBrowseButton.Hide;
  PublicCertInfoBitmapImage.Hide;
  PublicCertWarningBitmapImage.Hide;

  PrivateKeyLabel.Hide;
  PrivateKeyEdit.Hide;
  PrivateKeyBrowseButton.Hide;
  PrivateKeyInfoBitmapImage.Hide;
  PrivateKeyWarningBitmapImage.Hide;

  CaLabel.Hide;
  CaEdit.Hide;
  CABrowseButton.Hide;
  CaInfoBitmapImage.Hide;
  CaWarningBitmapImage.Hide;

  CommonNameLabel.Hide;
  CommonNameEdit.Hide;
  CommonNameInfoBitmapImage.Hide;

  TokenInfoBitmapImage.Hide;
  TokenWarningBitmapImage.Hide;

  if (EncryptionComboBox.Text = 'TLS') or (EncryptionComboBox.Text = 'Insecure TLS') then
  begin
    if PollerInitiatedCheckBox.Checked then
    begin
      // Show only cert/key/token for TLS + poller-initiated
      PublicCertLabel.Show;
      PublicCertEdit.Show;
      PublicCertBrowseButton.Show;
      PublicCertInfoBitmapImage.Show;

      PrivateKeyLabel.Show;
      PrivateKeyEdit.Show;
      PrivateKeyBrowseButton.Show;
      PrivateKeyInfoBitmapImage.Show;

      TokenInfoBitmapImage.Show;
    end
    else
    begin
      CaLabel.Show;
      CaEdit.Show;
      CABrowseButton.Show;
      CaInfoBitmapImage.Show;

      if EncryptionComboBox.Text = 'Insecure TLS' then
        begin
          CommonNameLabel.Show;
          CommonNameEdit.Show;
          CommonNameInfoBitmapImage.Show;
        end;

      TokenInfoBitmapImage.Show;
    end;
  end
end;

// Opens a file picker for selecting a .crt certificate file and, if chosen, assigns its path to PublicCertEdit.Text.
// Intended as the event handler for browsing the public certificate.
procedure PublicCertBrowseFile(Sender: TObject);
var
  Path : String;
begin
  if(GetOpenFileName('Select certificate file', Path, '','Certificate Files (*.crt)|*.crt', 'crt')) then
  begin
     PublicCertEdit.Text:= Path;
  end;
end;

// Opens a file dialog filtered for .key files and, if a file is selected, sets PrivateKeyEdit.Text to its path.
// Sender: the control that triggered the browse (not used within the procedure).
procedure PrivateKeyBrowseFile(Sender: TObject);
var
  Path : String;
begin
  if(GetOpenFileName('Select key file', Path, '','Key Files (*.key)|*.key', 'key')) then
  begin
     PrivateKeyEdit.Text:= Path;
  end;
end;

// Opens a file dialog to select a CA certificate (*.crt).
// If the user selects a file, updates CaEdit.Text with the chosen file path.
procedure CaBrowseFile(Sender: TObject);
var
  Path : String;
begin
  if(GetOpenFileName('Select ca file', Path, '','Certificate Files (*.crt)|*.crt', 'crt')) then
  begin
     CaEdit.Text:= Path;
  end;
end;

{ ────────────────────────────────────────────────────────────────────── }
{ ─────────────────────────── Utilities ──────────────────────────────── }
{ ────────────────────────────────────────────────────────────────────── }

// Returns the trimmed text from AgentInstanceComboBox, representing the selected agent instance name.
function GetInstanceArgs(Param: string): string;
begin
    Result := Trim(AgentInstanceComboBox.Text);
end;


// --- Enumerate Centreon agent registry subkeys ---
procedure LoadAgentRegistryKeys;
begin
  SetArrayLength(AgentNames, 0);
  RegGetSubkeyNames(HKEY_LOCAL_MACHINE, 'SOFTWARE\Centreon', AgentNames);
end;

function ServiceAlreadyExist(SvcName: string):Boolean;
var 
  Agent: Integer;
begin
  LoadAgentRegistryKeys;
  Result := False;
  if GetArrayLength(AgentNames) = 0 then
    Exit;

  for Agent := 0 to GetArrayLength(AgentNames) - 1 do
  begin
    if CompareText(AgentNames[Agent], SvcName) = 0 then
    begin
      Result := True;
      Exit;
    end;
  end;
end;

// Logs a message with a timestamp to 'installer_output.log' in the source executable's directory.
procedure LogToStdOutFile(Msg: String);
var
  LogFile: String;
  LogLine: String;
begin
  LogFile := AddBackslash(ExtractFileDir(ExpandConstant('{srcexe}'))) +
             'installer_output.log';
  LogLine := GetDateTimeString('yyyy-mm-dd hh:nn:ss', #0, #0) + ' - ' + Msg + #13#10;
  // Append log line to file, fail silently if error
  SaveStringToFile(LogFile, LogLine, True);
end;

// Checks if a specific command line parameter exists.
// Param: The parameter string to search for.
// Returns: True if the parameter is found among the command line arguments; otherwise, False.
function CmdLineParamExists(Param: string): Boolean;
var
  I: Integer;
begin
  for I := 1 to ParamCount do
    if CompareText(ParamStr(I), Param) = 0 then
    begin
      Result := True;
      Exit;
    end;
  Result := False;
end;

// Checks if the given path string is a valid Windows absolute path.
// Returns True if the path matches the expected format and contains only allowed characters.
function IsPathValid(Path: string): Boolean;
var
  I: Integer;
begin
  Path := Uppercase(Path);
  Result :=
    (Length(Path) >= 3) and
    (Path[1] >= 'A') and (Path[1] <= 'Z') and
    (Path[2] = ':') and
    (Path[3] = '\');

  if Result then
  begin
    for I := 3 to Length(Path) do
    begin
      case Path[I] of
        '0'..'9', 'A'..'Z', '\', ' ', '.', '-', '_', '(', ')':
          else 
        begin
          Result := False;
          Break;
        end;
      end;
    end;
  end;
end;

// Checks if the input string is in "host:port" format and validates host and port characters.
// Displays error messages and hints if the format or values are invalid.
function IsValidHostPortFormat(S: String): Boolean;
var
  Host, Port: String;
  ColonPos, i: Integer;
  c: Char;
begin
  Result := False;
  ColonPos := Pos(':', S);

  if (ColonPos > 1) and (ColonPos < Length(S)) then
  begin
    Host := Copy(S, 1, ColonPos - 1);
    Port := Copy(S, ColonPos + 1, Length(S));

    // Validate host characters
    for i := 1 to Length(Host) do
    begin
      c := Host[i];
      if not ((c >= 'a') and (c <= 'z') or
            (c >= 'A') and (c <= 'Z') or
            (c >= '0') and (c <= '9') or
            (c = '.') or (c = '-') or (c = '_')) then begin
      PollerWarningBitmapImage.Hint := 'Host contains invalid characters.';
      PollerWarningBitmapImage.Show;
      MsgBox('Host contains invalid characters.', mbError, MB_OK);
      Exit;
      end;
    end;

    // Validate port (must be all digits)
    if Length(Port) = 0 then begin
      PollerWarningBitmapImage.Hint := 'Missing port number after ":". Please provide a valid port.';
      PollerWarningBitmapImage.Show;
      MsgBox('Missing port number after ":". Please provide a valid port.', mbError, MB_OK);
      Exit;
    end;
    for i := 1 to Length(Port) do
    begin
      if Pos(Port[i], '0123456789') = 0 then begin
        PollerWarningBitmapImage.Hint := 'Invalid port: it must contain only digits (0-9).';
        PollerWarningBitmapImage.Show;
        MsgBox('Invalid port: it must contain only digits (0-9).', mbError, MB_OK);
        Exit;
      end;
    end;

    Result := True;
  end else begin
    PollerWarningBitmapImage.Hint := 'Invalid format. Please enter in the format "host:port", e.g. "localhost:8080".';
    PollerWarningBitmapImage.Show;
    MsgBox('Invalid format. Please enter in the format "host:port", e.g. "localhost:8080".', mbError, MB_OK);
    Exit;
  end;
end;

// Retrieves the current Windows computer name as a string.
// Returns an empty string if the computer name cannot be obtained.
function GetWindowsHostName: String;
var
  buf: String;
  size: LongWord;
begin
  size := 255;
  SetLength(buf, size);
  if GetComputerName(buf, size) then
    Result := Copy(buf, 1, size)
  else
    Result := '';
end;

// Reads Centreon Monitoring Agent configuration values from the Windows registry and populates the installer UI fields accordingly.
// For each expected registry value under 'SOFTWARE\Centreon\CentreonMonitoringAgent', attempts to read the value:
//   - If found, sets the corresponding UI control (edit box, combo box, or checkbox) with the value.
//   - If not found for 'host', uses the current Windows host name as a fallback.
// Handles both string and DWORD registry values, converting as needed for UI controls.
function ReadAgentRegistryValues(InstanceName: String): Boolean;
var
  RootKey: Integer;
  BaseKey: String;
  StrValue: String;
  DwordValue: DWORD;
begin
  RootKey := HKEY_LOCAL_MACHINE;
  BaseKey := 'SOFTWARE\Centreon\'+ InstanceName;
  if not RegKeyExists(RootKey, BaseKey) then
  begin
    Result := False;
    Exit;
  end;

  if RegQueryStringValue(RootKey, BaseKey, 'host', StrValue) then
    HostNameEdit.Text := StrValue
  else
    HostNameEdit.Text := GetWindowsHostName;

  if RegQueryStringValue(RootKey, BaseKey, 'host_template', StrValue) then
    HostTemplateEdit.Text := StrValue
  else
    HostTemplateEdit.Text := 'OS-Windows-Centreon-Monitoring-Agent-custom';

  if RegQueryStringValue(RootKey, BaseKey, 'endpoint', StrValue) then
    PollerEndpointEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'log_file', StrValue) then
    LogFileEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'log_level', StrValue) then
    LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf(StrValue);

  if RegQueryStringValue(RootKey, BaseKey, 'custom_check_file', StrValue) then
    CustomCheckEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'log_type', StrValue) then
    LogTypeComboBox.ItemIndex := LogTypeComboBox.Items.IndexOf(StrValue);

  if RegQueryStringValue(RootKey, BaseKey, 'public_cert', StrValue) then
    PublicCertEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'private_key', StrValue) then
    PrivateKeyEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'ca_certificate', StrValue) then
    CaEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'ca_name', StrValue) then
    CommonNameEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'token', StrValue) then
    TokenEdit.Text := StrValue;

  if RegQueryStringValue(RootKey, BaseKey, 'encryption', StrValue) then
  begin
    if StrValue = 'full' then
      EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('TLS')
    else if StrValue = 'insecure' then
      EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('Insecure TLS')
    else
      EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('No TLS');
  end;

  if RegQueryDWordValue(RootKey, BaseKey, 'log_max_file_size', DwordValue) then
    MaxFileEdit.Text := IntToStr(DwordValue);

  if RegQueryDWordValue(RootKey, BaseKey, 'log_max_files', DwordValue) then
    MaxNumberEdit.Text := IntToStr(DwordValue);

  if RegQueryDWordValue(RootKey, BaseKey, 'reversed_grpc_streaming', DwordValue) then
    PollerInitiatedCheckBox.Checked := (DwordValue <> 0);

  Result := True;
end;

// Writes Centreon Monitoring Agent configuration values to the Windows registry.
// This procedure sets string and DWORD values under the key 'SOFTWARE\Centreon\CentreonMonitoringAgent' in HKEY_LOCAL_MACHINE.
// The values are retrieved from various UI controls, including host, endpoint, log settings, certificates, and agent options.
procedure WriteAgentRegistryValues;
var
  RootKey: Integer;
  BaseKey: String;
begin
  RootKey := HKEY_LOCAL_MACHINE;
  // Use the selected Instance name so the agent (which builds the path with the Instance name) reads the same location.
  BaseKey := 'SOFTWARE\Centreon\' + GetInstanceArgs('');

  // Strings
  RegWriteStringValue(RootKey, BaseKey, 'host', HostNameEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'host_template', HostTemplateEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'endpoint', PollerEndpointEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'log_file', LogFileEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'custom_check_file', CustomCheckEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'log_level', LowerCase(LogLeveLComboBox.Text));
  RegWriteStringValue(RootKey, BaseKey, 'log_type', LowerCase(LogTypeComboBox.Text));
  RegWriteStringValue(RootKey, BaseKey, 'public_cert', PublicCertEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'private_key', PrivateKeyEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'ca_certificate', CaEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'ca_name', CommonNameEdit.Text);
  RegWriteStringValue(RootKey, BaseKey, 'token', TokenEdit.Text);

  if EncryptionComboBox.Text = 'TLS' then
    RegWriteStringValue(RootKey, BaseKey, 'encryption', 'full')
  else if EncryptionComboBox.Text = 'Insecure TLS' then
    RegWriteStringValue(RootKey, BaseKey, 'encryption', 'insecure')
  else
    RegWriteStringValue(RootKey, BaseKey, 'encryption', 'no');

  // DWORDs
  RegWriteDWordValue(RootKey, BaseKey, 'log_max_file_size', StrToIntDef(MaxFileEdit.Text, 10));
  RegWriteDWordValue(RootKey, BaseKey, 'log_max_files', StrToIntDef(MaxNumberEdit.Text, 10));
  RegWriteDWordValue(RootKey, BaseKey, 'reversed_grpc_streaming', Integer(PollerInitiatedCheckBox.Checked));
end;

// Restarts the CentreonMonitoringAgent service by stopping and then starting it.
// Uses 'sc.exe' to control the service and waits for each operation to complete.
// Includes a short delay between stop and start for graceful handling.
// Returns nothing; errors are not explicitly handled.
procedure RestartService;
var rc: Integer;
begin
  Exec(ExpandConstant('{sys}\sc.exe'), ' stop "' + GetInstanceArgs('') + '"', '', SW_HIDE, ewWaitUntilTerminated, rc);
  ; // small period
  Sleep(1000);
  Exec(ExpandConstant('{sys}\sc.exe'), ' start "' + GetInstanceArgs('') + '"', '', SW_HIDE, ewWaitUntilTerminated, rc);
end;

procedure OnServiceChange(Sender: TObject);
begin
  ReadAgentRegistryValues(AgentInstanceComboBox.Text);
  OnLogTypeChange(LogTypeComboBox);
end;

{ ────────────────────────────────────────────────────────────────────── }
{ ───────────────────────── Silent Mode ──────────────────────────────── }
{ ────────────────────────────────────────────────────────────────────── }
function IsSilentMode: Boolean;
begin
  Result := CmdLineParamExists('/silent') or CmdLineParamExists('/verysilent');
end;

// Loads installer parameters in silent mode from command-line arguments.
// Populates UI fields with provided or default values.
// Handles log type, log level, encryption, and certificate settings.
// Only executes if installer is running in silent mode.
procedure LoadSilentParameters;
var
  AgentInstanceParam: string;
begin
  if not IsSilentMode then Exit;

  // the parameter is already checked in CheckSilentParameterValidity before calling this function
  AgentInstanceParam := ExpandConstant('{param:agentinstance|}');
  if AgentInstanceParam <> '' then
    AgentInstanceComboBox.ItemIndex := AgentInstanceComboBox.Items.IndexOf(AgentInstanceParam)
  else
    AgentInstanceComboBox.ItemIndex := 0;

  ReadAgentRegistryValues(AgentInstanceComboBox.Text);
  if ExpandConstant('{param:host|}') <> '' then
    HostNameEdit.Text := ExpandConstant('{param:host|}');
  if ExpandConstant('{param:hosttemplate|}') <> '' then
    HostTemplateEdit.Text := ExpandConstant('{param:hosttemplate|}');
  if ExpandConstant('{param:endpoint|}') <> '' then
    PollerEndpointEdit.Text := ExpandConstant('{param:endpoint|}');

  // Log type (ComboBox)
  if ExpandConstant('{param:logtype|}') <> '' then
  begin
    if LowerCase(ExpandConstant('{param:logtype|}')) = 'file' then
      LogTypeComboBox.ItemIndex := 1
    else if LowerCase(ExpandConstant('{param:logtype|}')) = 'event-log' then
      LogTypeComboBox.ItemIndex := 0;
  end;

  // Log level (ComboBox)
  if ExpandConstant('{param:loglevel|}') <> '' then
  begin
    case LowerCase(ExpandConstant('{param:loglevel|}')) of
      'off':      LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Off');
      'critical': LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Critical');
      'error':    LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Error');
      'warning':  LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Warning');
      'info':     LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Info');
      'debug':    LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Debug');
      'trace':    LogLeveLComboBox.ItemIndex := LogLeveLComboBox.Items.IndexOf('Trace');
    end;
  end;

  if ExpandConstant('{param:customcheckfile|}') <> '' then
    CustomCheckEdit.Text := ExpandConstant('{param:customcheckfile|}');
  if ExpandConstant('{param:logfile|}') <> '' then
    LogFileEdit.Text := ExpandConstant('{param:logfile|}');
  if ExpandConstant('{param:maxfilesize|}') <> '' then
    MaxFileEdit.Text := ExpandConstant('{param:maxfilesize|}');
  if ExpandConstant('{param:maxnumber|}') <> '' then
    MaxNumberEdit.Text := ExpandConstant('{param:maxnumber|}');

  // PollerInitiatedCheckBox
  if ExpandConstant('{param:reverse|}') <> '' then
    PollerInitiatedCheckBox.Checked := (LowerCase(ExpandConstant('{param:reverse|}')) = 'true');

  // EncryptionCheckBox
  if ExpandConstant('{param:encryption|}') <> '' then
    case LowerCase(ExpandConstant('{param:encryption|no}')) of
      'full':     EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('TLS');
      'insecure': EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('Insecure TLS');
      else        EncryptionComboBox.ItemIndex := EncryptionComboBox.Items.IndexOf('No TLS');
    end;

  if ExpandConstant('{param:cert|}') <> '' then
    PublicCertEdit.Text := ExpandConstant('{param:cert|}');
  if ExpandConstant('{param:key|}') <> '' then
    PrivateKeyEdit.Text := ExpandConstant('{param:key|}');
  if ExpandConstant('{param:ca|}') <> '' then
    CaEdit.Text := ExpandConstant('{param:ca|}');
  if ExpandConstant('{param:commonname|}') <> '' then
    CommonNameEdit.Text := ExpandConstant('{param:commonname|}');
  if ExpandConstant('{param:token|}') <> '' then
    TokenEdit.Text := ExpandConstant('{param:token|}');
end;

// Checks validity of silent installation parameters.
// Validates required fields, formats, and values for host, endpoint, logging, encryption, and plugin source.
// Logs errors to standard output and returns False if any check fails.
// Returns True if all parameters are valid.
function CheckSilentParameterValidity: Boolean;
var
  AgentInstance: string;
  HostParam, EndpointParam, LogTypeParam, LogFileParam,Loglvl: string;
  Pluginsrc_p: string;
  EncryptionEnabled,PollerInitiatedEnabled: Boolean;
begin
  if not IsSilentMode then
  begin
    Result := True;
    Exit;
  end;

  AgentInstance := ExpandConstant('{param:agentinstance|}');
  HostParam := ExpandConstant('{param:host|}');
  EndpointParam := ExpandConstant('{param:endpoint|}');
  LogTypeParam := LowerCase(ExpandConstant('{param:logtype|event-log}'));
  LogFileParam := ExpandConstant('{param:logfile|}');
  EncryptionEnabled := (LowerCase(ExpandConstant('{param:encryption|no}')) = 'full') or
                       (LowerCase(ExpandConstant('{param:encryption|no}')) = 'insecure');
  PollerInitiatedEnabled := (LowerCase(ExpandConstant('{param:reverse|false}')) = 'true');

  // agent instance name
  if (AgentInstance <> '') and not(ServiceAlreadyExist(AgentInstance)) then
  begin
    LogToStdOutFile('Unknown instance agent: ' + AgentInstance);
    Result := False;
    Exit;
  end;

  // HOST
  if HostParam = '' then
  begin
    LogToStdOutFile('Missing parameter: /HOST');
    Result := False;
    Exit;
  end;

  // ENDPOINT format host:port
  if (Pos(':', EndpointParam) = 0) or (Copy(EndpointParam, Length(EndpointParam), 1) = ':') then
  begin
    LogToStdOutFile('Invalid format for /ENDPOINT. Expected "host:port".');
    Result := False;
    Exit;
  end;

  // if logtype = file → log file mandatory
  if LogTypeParam = 'file' then
  begin
    if not(IsPathValid(LogFileParam)) then
    begin
      LogToStdOutFile('Missing /LOGFILE when /LOGTYPE=file');
      Result := False;
      Exit;
    end;
  end;

  // check log level 
  Loglvl := LowerCase(ExpandConstant('{param:loglevel|error}'));
  if (Loglvl <> 'off') and (Loglvl <> 'critical') and (Loglvl <> 'error') and (Loglvl <> 'warning') and (Loglvl <> 'info') and (Loglvl <> 'debug') and (Loglvl <> 'trace') then
  begin
    LogToStdOutFile('Invalid /LOGLEVEL value. Allowed values: off|critical|error|warning|info|debug|trace');
    Result := False;
    Exit;
  end;

  // if encryption test cert key token
  if EncryptionEnabled then
  begin
    if PollerInitiatedEnabled then begin
      if not(IsPathValid(ExpandConstant('{param:cert|}'))) then
      begin
        LogToStdOutFile('Missing or invalid /CERT path. Provide a valid absolute path (e.g., C:\certs\public.crt) when /ENCRYPTION=full/insecure.');
        Result := False;
        Exit;
      end;

      if not(IsPathValid(ExpandConstant('{param:key|}'))) then
      begin
        LogToStdOutFile('Missing or invalid /KEY path. Provide a valid absolute path (e.g., C:\certs\private.key) when /ENCRYPTION=full/insecure.');
        Result := False;
        Exit;
      end;
     end
    else begin
      if (ExpandConstant('{param:ca|}') <> '') and not(IsPathValid(ExpandConstant('{param:ca|}'))) then
        begin
          LogToStdOutFile('Invalid /CA path. Provide a valid absolute path (e.g., C:\certs\ca.crt) when /ENCRYPTION=full/insecure');
          Result := False;
          Exit;
        end;
    end;
     
  end;

  Pluginsrc_p := LowerCase(ExpandConstant('{param:pluginsrc|auto}'));
  if (Pluginsrc_p <> 'auto') and (Pluginsrc_p <> 'embedded') then begin
    LogToStdOutFile('Invalid /PLUGINSRC value. Use auto|embedded.');
    Result := False;
    Exit;
  end;

  // all good
  Result := True;
end;

{ ────────────────────────────────────────────────────────────────────── }
{ ───────────────────────── Start Wizard ─────────────────────────────── }
{ ────────────────────────────────────────────────────────────────────── }
// Initializes the setup process, handles silent mode and version display.
// Returns True if silent parameters are valid, otherwise exits with error.
function InitializeSetup(): Boolean;
begin
  // ── Silent mode
  Result := CheckSilentParameterValidity;

  if not Result then
  ExitProcess(1)  
end;

// Initializes the Centreon Agent installer wizard, configuring custom pages and UI elements for agent setup.
procedure InitializeWizard;
var
Infobmp: String;
WarningBmp: String;
Index : Integer;
begin

// ── Wizard config
  WizardForm.ClientWidth := 630;
  WizardForm.ClientHeight := 570;

// ── Load resources 
  ExtractTemporaryFile('info_icon.bmp');
  Infobmp := ExpandConstant('{tmp}\info_icon.bmp');
  ExtractTemporaryFile('warning_icon.bmp');
  WarningBmp := ExpandConstant('{tmp}\warning_icon.bmp');
// ── Config page 
  ConfigPage := CreateCustomPage(wpSelectComponents, 'Centreon Agent Configuration', 'Please provide the required information to connect agent.');
  // ──── Service Name

  AgentInstanceLabel := TNewStaticText.Create(ConfigPage);
  AgentInstanceLabel.Parent := ConfigPage.Surface;
  AgentInstanceLabel.Top := ScaleY(3);
  AgentInstanceLabel.Caption := 'Agent instance:';

  AgentInstanceComboBox := TNewComboBox.Create(ConfigPage);
  AgentInstanceComboBox.top := AgentInstanceLabel.Top - ScaleY(3);
  AgentInstanceComboBox.Width := ConfigPage.SurfaceWidth div 2;
  AgentInstanceComboBox.Parent := ConfigPage.Surface;
  AgentInstanceComboBox.Style := csDropDownList;
  LoadAgentRegistryKeys;
  if GetArrayLength(AgentNames) = 0 then
    ExitProcess(1); // No Centreon agent found, exit
    
  for Index := 0 to GetArrayLength(AgentNames) - 1 do
    AgentInstanceComboBox.Items.Add(AgentNames[Index]);

  AgentInstanceComboBox.ItemIndex := 0;
  AgentInstanceComboBox.OnChange := @OnServiceChange;
  // ──── Host Name

  HostLabel := TNewStaticText.Create(ConfigPage);
  HostLabel.Parent := ConfigPage.Surface;
  HostLabel.Top := AgentInstanceLabel.Top + ScaleY(30);
  HostLabel.Caption := 'Host name in Centreon:';

  HostNameEdit := TNewEdit.Create(ConfigPage);
  HostNameEdit.Parent := ConfigPage.Surface;
  HostNameEdit.Top := HostLabel.Top - ScaleY(3);
  HostNameEdit.Left := HostLabel.Width + ScaleX(20);
  HostNameEdit.Width := ConfigPage.SurfaceWidth div 2;
  HostNameEdit.Text := 'my-centreon-host';

  AgentInstanceComboBox.Left := HostNameEdit.Left;

  HostInfoBitmapImage := TBitmapImage.Create(ConfigPage);
  HostInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  HostInfoBitmapImage.Parent := ConfigPage.Surface;
  HostInfoBitmapImage.Top := HostLabel.Top- ScaleY(3);
  HostInfoBitmapImage.Left := HostNameEdit.Left + HostNameEdit.Width + ScaleX(4);
  HostInfoBitmapImage.Center := True;
  HostInfoBitmapImage.Width := 24;
  HostInfoBitmapImage.Height := 24;
  HostInfoBitmapImage.Stretch := True;
  HostInfoBitmapImage.Cursor := crHand;
  HostInfoBitmapImage.OnClick := @BitmapImageOnClick;

  HostWarningBitmapImage := TBitmapImage.Create(ConfigPage);
  HostWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  HostWarningBitmapImage.Parent := ConfigPage.Surface;
  HostWarningBitmapImage.Top := HostLabel.Top- ScaleY(3);
  HostWarningBitmapImage.Left := HostInfoBitmapImage.Left + HostInfoBitmapImage.Width + ScaleX(4);
  HostWarningBitmapImage.Center := True;
  HostWarningBitmapImage.Width := 24;
  HostWarningBitmapImage.Height := 24;
  HostWarningBitmapImage.Stretch := True;
  HostWarningBitmapImage.Cursor := crHand;
  HostWarningBitmapImage.ShowHint := True;
  HostWarningBitmapImage.Hide;

  // ──── Host Template

  HostTemplateLabel := TNewStaticText.Create(ConfigPage);
  HostTemplateLabel.Parent := ConfigPage.Surface;
  HostTemplateLabel.Top := HostLabel.Top + ScaleY(30);
  HostTemplateLabel.Caption := 'Host template:';

  HostTemplateEdit := TNewEdit.Create(ConfigPage);
  HostTemplateEdit.Parent := ConfigPage.Surface;
  HostTemplateEdit.Top := HostTemplateLabel.Top - ScaleY(3);
  HostTemplateEdit.Left := HostNameEdit.Left;
  HostTemplateEdit.Width := ConfigPage.SurfaceWidth div 2;
  HostTemplateEdit.Text := 'OS-Windows-Centreon-Monitoring-Agent-custom';

  HostTemplateInfoBitmapImage := TBitmapImage.Create(ConfigPage);
  HostTemplateInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  HostTemplateInfoBitmapImage.Parent := ConfigPage.Surface;
  HostTemplateInfoBitmapImage.Top := HostTemplateLabel.Top- ScaleY(3);
  HostTemplateInfoBitmapImage.Left := HostTemplateEdit.Left + HostTemplateEdit.Width + ScaleX(4);
  HostTemplateInfoBitmapImage.Center := True;
  HostTemplateInfoBitmapImage.Width := 24;
  HostTemplateInfoBitmapImage.Height := 24;
  HostTemplateInfoBitmapImage.Stretch := True;
  HostTemplateInfoBitmapImage.Cursor := crHand;
  HostTemplateInfoBitmapImage.OnClick := @BitmapImageOnClick;

  // ──── Poller Endpoint

  PollerLabel := TNewStaticText.Create(ConfigPage);
  PollerLabel.Parent := ConfigPage.Surface;
  PollerLabel.Top := HostTemplateLabel.Top + ScaleY(30);
  PollerLabel.Caption := 'Poller endpoint:';

  PollerEndpointEdit := TNewEdit.Create(ConfigPage);
  PollerEndpointEdit.Parent := ConfigPage.Surface;
  PollerEndpointEdit.Top := PollerLabel.Top - ScaleY(3);
  PollerEndpointEdit.Left := HostNameEdit.Left;
  PollerEndpointEdit.Width := ConfigPage.SurfaceWidth div 2;
  PollerEndpointEdit.Text := '127.0.0.1:4317';

  PollerInfoBitmapImage := TBitmapImage.Create(ConfigPage);
  PollerInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  PollerInfoBitmapImage.Parent := ConfigPage.Surface;
  PollerInfoBitmapImage.Top := PollerLabel.Top- ScaleY(3);
  PollerInfoBitmapImage.Left := PollerEndpointEdit.Left + PollerEndpointEdit.Width + ScaleX(4);
  PollerInfoBitmapImage.Center := True;
  PollerInfoBitmapImage.Width := 24;
  PollerInfoBitmapImage.Height := 24;
  PollerInfoBitmapImage.Stretch := True;
  PollerInfoBitmapImage.Cursor := crHand;
  PollerInfoBitmapImage.OnClick := @BitmapImageOnClick;

  PollerWarningBitmapImage := TBitmapImage.Create(ConfigPage);
  PollerWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  PollerWarningBitmapImage.Parent := ConfigPage.Surface;
  PollerWarningBitmapImage.Top := PollerLabel.Top - ScaleY(3);
  PollerWarningBitmapImage.Left := PollerInfoBitmapImage.Left + PollerInfoBitmapImage.Width + ScaleX(4);
  PollerWarningBitmapImage.Center := True;
  PollerWarningBitmapImage.Width := 24;
  PollerWarningBitmapImage.Height := 24;
  PollerWarningBitmapImage.Stretch := True;
  PollerWarningBitmapImage.Cursor := crHand;
  PollerWarningBitmapImage.ShowHint := True;
  PollerWarningBitmapImage.Hide;

  // ──── Poller-initiated connection

  PollerInitiatedCheckBox := TNewCheckBox.Create(ConfigPage);
  PollerInitiatedCheckBox.Parent := ConfigPage.Surface;
  PollerInitiatedCheckBox.Top := PollerLabel.Top + ScaleY(30);
  PollerInitiatedCheckBox.Caption := 'Poller-initiated connection';
  PollerInitiatedCheckBox.Width := WizardForm.CalculateButtonWidth([PollerInitiatedCheckBox.Caption]);
  PollerInitiatedCheckBox.Checked := False;
  PollerInitiatedCheckBox.OnClick := @OnPollerInitiatedToggle;

  PollerInitiatedInfoBitmapImage := TBitmapImage.Create(ConfigPage);
  PollerInitiatedInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  PollerInitiatedInfoBitmapImage.Parent := ConfigPage.Surface;
  PollerInitiatedInfoBitmapImage.Top := PollerInitiatedCheckBox.Top - ScaleY(3);
  PollerInitiatedInfoBitmapImage.Left := PollerEndpointEdit.Width - ScaleX(4);
  PollerInitiatedInfoBitmapImage.Center := True;
  PollerInitiatedInfoBitmapImage.Width := 24;
  PollerInitiatedInfoBitmapImage.Height := 24;
  PollerInitiatedInfoBitmapImage.Stretch := True;
  PollerInitiatedInfoBitmapImage.Cursor := crHand;
  PollerInitiatedInfoBitmapImage.OnClick := @BitmapImageOnClick;

    // ──── CustomCheck
  
  CustomCheckLabel := TNewStaticText.Create(ConfigPage);
  CustomCheckLabel.Parent := ConfigPage.Surface;
  CustomCheckLabel.Top := PollerInitiatedCheckBox.Top + ScaleY(30);
  CustomCheckLabel.Caption := 'Custom check file:';

  CustomCheckEdit := TNewEdit.Create(ConfigPage);
  CustomCheckEdit.Parent := ConfigPage.Surface;
  CustomCheckEdit.Top := CustomCheckLabel.Top - ScaleY(3);
  CustomCheckEdit.Left := HostNameEdit.Left;
  CustomCheckEdit.Width := ConfigPage.SurfaceWidth div 2;
  CustomCheckEdit.Text := '';

  CustomCheckBrowseButton := TButton.Create(ConfigPage);
  CustomCheckBrowseButton.Parent := ConfigPage.Surface;
  CustomCheckBrowseButton.Caption := '...';
  CustomCheckBrowseButton.Left := CustomCheckEdit.Left + CustomCheckEdit.Width + ScaleX(4) ;
  CustomCheckBrowseButton.Top := CustomCheckEdit.Top - ScaleY(1);
  CustomCheckBrowseButton.Width := 35;
  CustomCheckBrowseButton.Height := CustomCheckEdit.Height + ScaleY(2);
  CustomCheckBrowseButton.OnClick := @CheckFileBrowseFile;


  // ──── Log type

  LogTypeLabel := TNewStaticText.Create(ConfigPage);
  LogTypeLabel.Parent := ConfigPage.Surface;
  LogTypeLabel.Top := CustomCheckLabel.Top + ScaleY(30);
  LogTypeLabel.Caption := 'Log type:';

  LogTypeComboBox := TNewComboBox.Create(ConfigPage);
  LogTypeComboBox.Width := ConfigPage.SurfaceWidth;
  LogTypeComboBox.top := CustomCheckLabel.Top + ScaleY(27);
  LogTypeComboBox.Width := ConfigPage.SurfaceWidth div 2;
  LogTypeComboBox.Left := HostNameEdit.Left;
  LogTypeComboBox.Parent := ConfigPage.Surface;
  LogTypeComboBox.Style := csDropDownList;
  LogTypeComboBox.Items.Add('Event-log');
  LogTypeComboBox.Items.Add('File');
  LogTypeComboBox.ItemIndex := 0;
  LogTypeComboBox.OnChange := @OnLogTypeChange;

  // ──── Log level

  LogLeveLabel := TNewStaticText.Create(ConfigPage);
  LogLeveLabel.Parent := ConfigPage.Surface;
  LogLeveLabel.Top := LogTypeLabel.Top + ScaleY(30);
  LogLeveLabel.Caption := 'Log level:';

  LogLeveLComboBox := TNewComboBox.Create(ConfigPage);
  LogLeveLComboBox.Width := ConfigPage.SurfaceWidth;
  LogLeveLComboBox.top := LogTypeLabel.Top + ScaleY(27);
  LogLeveLComboBox.Width := ConfigPage.SurfaceWidth div 2;
  LogLeveLComboBox.Left := HostNameEdit.Left;
  LogLeveLComboBox.Parent := ConfigPage.Surface;
  LogLeveLComboBox.Style := csDropDownList;
  LogLeveLComboBox.Items.Add('Off');
  LogLeveLComboBox.Items.Add('Critical');
  LogLeveLComboBox.Items.Add('Error');
  LogLeveLComboBox.Items.Add('Warning');
  LogLeveLComboBox.Items.Add('Info');
  LogLeveLComboBox.Items.Add('Debug');
  LogLeveLComboBox.Items.Add('Trace');
  LogLeveLComboBox.ItemIndex := 2;

  // ──── Log file

  LogFileLabel := TNewStaticText.Create(ConfigPage);
  LogFileLabel.Parent := ConfigPage.Surface;
  LogFileLabel.Top := LogLeveLabel.Top + ScaleY(30);
  LogFileLabel.Caption := 'Log file:';
  LogFileLabel.Hide;

  LogFileEdit := TNewEdit.Create(ConfigPage);
  LogFileEdit.Parent := ConfigPage.Surface;
  LogFileEdit.Top := LogFileLabel.Top - ScaleY(3);
  LogFileEdit.Left := HostNameEdit.Left;
  LogFileEdit.Width := ConfigPage.SurfaceWidth div 2;
  LogFileEdit.Text := '';
  LogFileEdit.Hide;

  LogFileBrowseButton := TButton.Create(ConfigPage);
  LogFileBrowseButton.Parent := ConfigPage.Surface;
  LogFileBrowseButton.Caption := '...';
  LogFileBrowseButton.Left := LogFileEdit.Left + LogFileEdit.Width + ScaleX(4) ;
  LogFileBrowseButton.Top := LogFileEdit.Top - ScaleY(1);
  LogFileBrowseButton.Width := 35;
  LogFileBrowseButton.Height := LogFileEdit.Height + ScaleY(2);
  LogFileBrowseButton.OnClick := @LogFileBrowseFile;
  LogFileBrowseButton.Hide;

  LogFileWarningBitmapImage := TBitmapImage.Create(ConfigPage);
  LogFileWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  LogFileWarningBitmapImage.Parent := ConfigPage.Surface;
  LogFileWarningBitmapImage.Top := LogFileLabel.Top- ScaleY(3);
  LogFileWarningBitmapImage.Left := LogFileBrowseButton.Left + LogFileBrowseButton.Width + ScaleX(4);
  LogFileWarningBitmapImage.Center := True;
  LogFileWarningBitmapImage.Width := 24;
  LogFileWarningBitmapImage.Height := 24;
  LogFileWarningBitmapImage.Stretch := True;
  LogFileWarningBitmapImage.Cursor := crHand;
  LogFileWarningBitmapImage.ShowHint := True;
  LogFileWarningBitmapImage.Hide;

  // ──── Max file size

  MaxFileLabel := TNewStaticText.Create(ConfigPage);
  MaxFileLabel.Parent := ConfigPage.Surface;
  MaxFileLabel.Top := LogFileLabel.Top + ScaleY(30);
  MaxFileLabel.Caption := 'Max file size (Mo):';
  MaxFileLabel.Hide;

  MaxFileEdit := TNewEdit.Create(ConfigPage);
  MaxFileEdit.Parent := ConfigPage.Surface;
  MaxFileEdit.Top := MaxFileLabel.Top - ScaleY(3);
  MaxFileEdit.Left := HostNameEdit.Left;
  MaxFileEdit.Width := ConfigPage.SurfaceWidth div 2;
  MaxFileEdit.Text := '';
  MaxFileEdit.OnKeyPress := @NumbersOnlyKeyPress;
  MaxFileEdit.Hide;

  MaxFileInfoBitmapImage := TBitmapImage.Create(ConfigPage);
  MaxFileInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  MaxFileInfoBitmapImage.Parent := ConfigPage.Surface;
  MaxFileInfoBitmapImage.Top := MaxFileLabel.Top - ScaleY(3);
  MaxFileInfoBitmapImage.Left := MaxFileEdit.Left + MaxFileEdit.Width + ScaleX(4);
  MaxFileInfoBitmapImage.Center := True;
  MaxFileInfoBitmapImage.Width := 24;
  MaxFileInfoBitmapImage.Height := 24;
  MaxFileInfoBitmapImage.Stretch := True;
  MaxFileInfoBitmapImage.Cursor := crHand;
  MaxFileInfoBitmapImage.OnClick := @BitmapImageOnClick;
  MaxFileInfoBitmapImage.Hide;

  // ──── Max number of files

  MaxNumberLabel := TNewStaticText.Create(ConfigPage);
  MaxNumberLabel.Parent := ConfigPage.Surface;
  MaxNumberLabel.Top := MaxFileLabel.Top + ScaleY(30);
  MaxNumberLabel.Caption := 'Max number of files:';
  MaxNumberLabel.Hide;

  MaxNumberEdit := TNewEdit.Create(ConfigPage);
  MaxNumberEdit.Parent := ConfigPage.Surface;
  MaxNumberEdit.Top := MaxNumberLabel.Top - ScaleY(3);
  MaxNumberEdit.Left := HostNameEdit.Left;
  MaxNumberEdit.Width := ConfigPage.SurfaceWidth div 2;
  MaxNumberEdit.Text := '';
  MaxNumberEdit.OnKeyPress := @NumbersOnlyKeyPress;
  MaxNumberEdit.Hide;

  MaxNumberInfoBitmapImage := TBitmapImage.Create(ConfigPage);
  MaxNumberInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  MaxNumberInfoBitmapImage.Parent := ConfigPage.Surface;
  MaxNumberInfoBitmapImage.Top := MaxNumberLabel.Top - ScaleY(3);
  MaxNumberInfoBitmapImage.Left := MaxNumberEdit.Left + MaxNumberEdit.Width + ScaleX(4);
  MaxNumberInfoBitmapImage.Center := True;
  MaxNumberInfoBitmapImage.Width := 24;
  MaxNumberInfoBitmapImage.Height := 24;
  MaxNumberInfoBitmapImage.Stretch := True;
  MaxNumberInfoBitmapImage.Cursor := crHand;
  MaxNumberInfoBitmapImage.OnClick := @BitmapImageOnClick;
  MaxNumberInfoBitmapImage.Hide;


// ── Encryption page
  EncryptionPage := CreateCustomPage(ConfigPage.ID,
  'Centreon Agent Configuration',
  'Connection security for agent.');
  // ──── Encryption

  EncryptionLabel := TNewStaticText.Create(EncryptionPage);
  EncryptionLabel.Parent := EncryptionPage.Surface;
  EncryptionLabel.Top := AgentInstanceLabel.Top + ScaleY(2);
  EncryptionLabel.Caption := 'Encryption:';

  EncryptionComboBox := TNewComboBox.Create(EncryptionPage);
  EncryptionComboBox.top := EncryptionLabel.Top - ScaleY(2);
  EncryptionComboBox.Width := ScaleX(90);
  EncryptionComboBox.Left := EncryptionLabel.Left + EncryptionLabel.Width + ScaleX(10) ;
  EncryptionComboBox.Parent := EncryptionPage.Surface;
  EncryptionComboBox.Style := csDropDownList;
  EncryptionComboBox.Items.Add('TLS');
  EncryptionComboBox.Items.Add('Insecure TLS');
  EncryptionComboBox.Items.Add('No TLS');
  EncryptionComboBox.ItemIndex := 0;
  EncryptionComboBox.OnChange := @OnEncryptionChange;

  // ──── Token

  TokenLabel := TNewStaticText.Create(EncryptionPage);
  TokenLabel.Parent := EncryptionPage.Surface;
  TokenLabel.Top := EncryptionLabel.Top + ScaleY(40);
  TokenLabel.Caption := 'Token:';


  TokenEdit := TNewEdit.Create(EncryptionPage);
  TokenEdit.Parent := EncryptionPage.Surface;
  TokenEdit.Top := TokenLabel.Top - ScaleY(3);
  TokenEdit.Left := HostNameEdit.Left;
  TokenEdit.Width := ConfigPage.SurfaceWidth div 2;
  TokenEdit.Text := '';


  TokenInfoBitmapImage := TBitmapImage.Create(EncryptionPage);
  TokenInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  TokenInfoBitmapImage.Parent := EncryptionPage.Surface;
  TokenInfoBitmapImage.Top := TokenLabel.Top - ScaleY(3);
  TokenInfoBitmapImage.Left := TokenEdit.Left + TokenEdit.Width + ScaleX(4);
  TokenInfoBitmapImage.Center := True;
  TokenInfoBitmapImage.Width := 24;
  TokenInfoBitmapImage.Height := 24;
  TokenInfoBitmapImage.Stretch := True;
  TokenInfoBitmapImage.Cursor := crHand;
  TokenInfoBitmapImage.OnClick := @BitmapImageOnClick;
  TokenInfoBitmapImage.Hide;

  TokenWarningBitmapImage := TBitmapImage.Create(EncryptionPage);
  TokenWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  TokenWarningBitmapImage.Parent := EncryptionPage.Surface;
  TokenWarningBitmapImage.Top := TokenLabel.Top- ScaleY(3);
  TokenWarningBitmapImage.Left := TokenInfoBitmapImage.Left + TokenInfoBitmapImage.Width + ScaleX(4);
  TokenWarningBitmapImage.Center := True;
  TokenWarningBitmapImage.Width := 24;
  TokenWarningBitmapImage.Height := 24;
  TokenWarningBitmapImage.Stretch := True;
  TokenWarningBitmapImage.Cursor := crHand;
  TokenWarningBitmapImage.ShowHint := True;
  TokenWarningBitmapImage.Hide;


  // ──── Public certificate

  PublicCertLabel := TNewStaticText.Create(EncryptionPage);
  PublicCertLabel.Parent := EncryptionPage.Surface;
  PublicCertLabel.Top := TokenLabel.Top + ScaleY(30);
  PublicCertLabel.Caption := 'Public certificate:';
  PublicCertLabel.Hide;

  PublicCertEdit := TNewEdit.Create(EncryptionPage);
  PublicCertEdit.Parent := EncryptionPage.Surface;
  PublicCertEdit.Top := PublicCertLabel.Top - ScaleY(3);
  PublicCertEdit.Left := HostNameEdit.Left;
  PublicCertEdit.Width := EncryptionPage.SurfaceWidth div 2;
  PublicCertEdit.Text := '';
  PublicCertEdit.Hide;

  PublicCertBrowseButton := TButton.Create(EncryptionPage);
  PublicCertBrowseButton.Parent := EncryptionPage.Surface;
  PublicCertBrowseButton.Caption := '...';
  PublicCertBrowseButton.Left := PublicCertEdit.Left + PublicCertEdit.Width + ScaleX(4);
  PublicCertBrowseButton.Top := PublicCertEdit.Top - ScaleY(1);
  PublicCertBrowseButton.Width := 35;
  PublicCertBrowseButton.Height := PublicCertEdit.Height + ScaleY(2);
  PublicCertBrowseButton.OnClick := @PublicCertBrowseFile;
  PublicCertBrowseButton.Hide;

  PublicCertInfoBitmapImage := TBitmapImage.Create(EncryptionPage);
  PublicCertInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  PublicCertInfoBitmapImage.Parent := EncryptionPage.Surface;
  PublicCertInfoBitmapImage.Top := PublicCertLabel.Top - ScaleY(3);
  PublicCertInfoBitmapImage.Left := PublicCertBrowseButton.Left + PublicCertBrowseButton.Width + ScaleX(4);
  PublicCertInfoBitmapImage.Center := True;
  PublicCertInfoBitmapImage.Width := 24;
  PublicCertInfoBitmapImage.Height := 24;
  PublicCertInfoBitmapImage.Stretch := True;
  PublicCertInfoBitmapImage.Cursor := crHand;
  PublicCertInfoBitmapImage.OnClick := @BitmapImageOnClick;
  PublicCertInfoBitmapImage.Hide;

  PublicCertWarningBitmapImage := TBitmapImage.Create(EncryptionPage);
  PublicCertWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  PublicCertWarningBitmapImage.Parent := EncryptionPage.Surface;
  PublicCertWarningBitmapImage.Top := PublicCertLabel.Top - ScaleY(3);
  PublicCertWarningBitmapImage.Left := PublicCertInfoBitmapImage.Left + PublicCertInfoBitmapImage.Width + ScaleX(4);
  PublicCertWarningBitmapImage.Center := True;
  PublicCertWarningBitmapImage.Width := 24;
  PublicCertWarningBitmapImage.Height := 24;
  PublicCertWarningBitmapImage.Stretch := True;
  PublicCertWarningBitmapImage.Cursor := crHand;
  PublicCertWarningBitmapImage.ShowHint := True;
  PublicCertWarningBitmapImage.Hide;

  // ──── Private key

  PrivateKeyLabel := TNewStaticText.Create(EncryptionPage);
  PrivateKeyLabel.Parent := EncryptionPage.Surface;
  PrivateKeyLabel.Top := PublicCertLabel.Top + ScaleY(30);
  PrivateKeyLabel.Caption := 'Private key:';
  PrivateKeyLabel.Hide;

  PrivateKeyEdit := TNewEdit.Create(EncryptionPage);
  PrivateKeyEdit.Parent := EncryptionPage.Surface;
  PrivateKeyEdit.Top := PrivateKeyLabel.Top - ScaleY(3);
  PrivateKeyEdit.Left := HostNameEdit.Left;
  PrivateKeyEdit.Width := EncryptionPage.SurfaceWidth div 2;
  PrivateKeyEdit.Text := '';
  PrivateKeyEdit.Hide;

  PrivateKeyBrowseButton := TButton.Create(EncryptionPage);
  PrivateKeyBrowseButton.Parent := EncryptionPage.Surface;
  PrivateKeyBrowseButton.Caption := '...';
  PrivateKeyBrowseButton.Left := PrivateKeyEdit.Left + PrivateKeyEdit.Width + ScaleX(4);
  PrivateKeyBrowseButton.Top := PrivateKeyEdit.Top - ScaleY(1);
  PrivateKeyBrowseButton.Width := 35;
  PrivateKeyBrowseButton.Height := PrivateKeyEdit.Height + ScaleY(2);
  PrivateKeyBrowseButton.OnClick := @PrivateKeyBrowseFile;
  PrivateKeyBrowseButton.Hide;

  PrivateKeyInfoBitmapImage := TBitmapImage.Create(EncryptionPage);
  PrivateKeyInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  PrivateKeyInfoBitmapImage.Parent := EncryptionPage.Surface;
  PrivateKeyInfoBitmapImage.Top := PrivateKeyLabel.Top - ScaleY(3);
  PrivateKeyInfoBitmapImage.Left := PrivateKeyBrowseButton.Left + PrivateKeyBrowseButton.Width + ScaleX(4);
  PrivateKeyInfoBitmapImage.Center := True;
  PrivateKeyInfoBitmapImage.Width := 24;
  PrivateKeyInfoBitmapImage.Height := 24;
  PrivateKeyInfoBitmapImage.Stretch := True;
  PrivateKeyInfoBitmapImage.Cursor := crHand;
  PrivateKeyInfoBitmapImage.OnClick := @BitmapImageOnClick;
  PrivateKeyInfoBitmapImage.Hide;

  PrivateKeyWarningBitmapImage := TBitmapImage.Create(EncryptionPage);
  PrivateKeyWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  PrivateKeyWarningBitmapImage.Parent := EncryptionPage.Surface;
  PrivateKeyWarningBitmapImage.Top := PrivateKeyLabel.Top- ScaleY(3);
  PrivateKeyWarningBitmapImage.Left := PrivateKeyInfoBitmapImage.Left + PrivateKeyInfoBitmapImage.Width + ScaleX(4);
  PrivateKeyWarningBitmapImage.Center := True;
  PrivateKeyWarningBitmapImage.Width := 24;
  PrivateKeyWarningBitmapImage.Height := 24;
  PrivateKeyWarningBitmapImage.Stretch := True;
  PrivateKeyWarningBitmapImage.Cursor := crHand;
  PrivateKeyWarningBitmapImage.ShowHint := True;
  PrivateKeyWarningBitmapImage.Hide;

  // ──── Ca

  CaLabel := TNewStaticText.Create(EncryptionPage);
  CaLabel.Parent := EncryptionPage.Surface;
  CaLabel.Top := TokenLabel.Top + ScaleY(30);
  CaLabel.Caption := 'CA:';
  CaLabel.Hide;

  CaEdit := TNewEdit.Create(EncryptionPage);
  CaEdit.Parent := EncryptionPage.Surface;
  CaEdit.Top := CaLabel.Top - ScaleY(3);
  CaEdit.Left := HostNameEdit.Left;
  CaEdit.Width := EncryptionPage.SurfaceWidth div 2;
  CaEdit.Text := '';
  CaEdit.Hide;

  CaBrowseButton := TButton.Create(EncryptionPage);
  CaBrowseButton.Parent := EncryptionPage.Surface;
  CaBrowseButton.Caption := '...';
  CaBrowseButton.Left := CaEdit.Left + CaEdit.Width + 8;
  CaBrowseButton.Top := CaEdit.Top - ScaleY(1);
  CaBrowseButton.Width := 35;
  CaBrowseButton.Height := CaEdit.Height + ScaleY(2);
  CaBrowseButton.OnClick := @CaBrowseFile;
  CaBrowseButton.Hide;

  CaInfoBitmapImage := TBitmapImage.Create(EncryptionPage);
  CaInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  CaInfoBitmapImage.Parent := EncryptionPage.Surface;
  CaInfoBitmapImage.Top := CaLabel.Top - ScaleY(3);
  CaInfoBitmapImage.Left := CaBrowseButton.Left + CaBrowseButton.Width + ScaleX(4);
  CaInfoBitmapImage.Center := True;
  CaInfoBitmapImage.Width := 24;
  CaInfoBitmapImage.Height := 24;
  CaInfoBitmapImage.Stretch := True;
  CaInfoBitmapImage.Cursor := crHand;
  CaInfoBitmapImage.OnClick := @BitmapImageOnClick;
  CaInfoBitmapImage.Hide;

  CaWarningBitmapImage := TBitmapImage.Create(EncryptionPage);
  CaWarningBitmapImage.Bitmap.LoadFromFile(Warningbmp);
  CaWarningBitmapImage.Parent := EncryptionPage.Surface;
  CaWarningBitmapImage.Top := CaLabel.Top- ScaleY(3);
  CaWarningBitmapImage.Left := CaInfoBitmapImage.Left + CaInfoBitmapImage.Width + ScaleX(4);
  CaWarningBitmapImage.Center := True;
  CaWarningBitmapImage.Width := 24;
  CaWarningBitmapImage.Height := 24;
  CaWarningBitmapImage.Stretch := True;
  CaWarningBitmapImage.Cursor := crHand;
  CaWarningBitmapImage.ShowHint := True;
  CaWarningBitmapImage.Hide;

  
  // ──── Ca common name (CN)

  CommonNameLabel:= TNewStaticText.Create(EncryptionPage);
  CommonNameLabel.Parent := EncryptionPage.Surface;
  CommonNameLabel.Top := CaLabel.Top + ScaleY(30);
  CommonNameLabel.Caption := 'CA Common Name (CN):';
  CommonNameLabel.Hide;

  CommonNameEdit := TNewEdit.Create(EncryptionPage);
  CommonNameEdit.Parent := EncryptionPage.Surface;
  CommonNameEdit.Top := CommonNameLabel.Top - ScaleY(3);
  CommonNameEdit.Left := HostNameEdit.Left;
  CommonNameEdit.Width := EncryptionPage.SurfaceWidth div 2;
  CommonNameEdit.Text := '';
  CommonNameEdit.Hide;

  CommonNameInfoBitmapImage := TBitmapImage.Create(EncryptionPage);
  CommonNameInfoBitmapImage.Bitmap.LoadFromFile(Infobmp);
  CommonNameInfoBitmapImage.Parent := EncryptionPage.Surface;
  CommonNameInfoBitmapImage.Top := CommonNameLabel.Top - ScaleY(3);
  CommonNameInfoBitmapImage.Left := CommonNameEdit.Left + CommonNameEdit.Width + ScaleX(4);
  CommonNameInfoBitmapImage.Center := True;
  CommonNameInfoBitmapImage.Width := 24;
  CommonNameInfoBitmapImage.Height := 24;
  CommonNameInfoBitmapImage.Stretch := True;
  CommonNameInfoBitmapImage.Cursor := crHand;
  CommonNameInfoBitmapImage.OnClick := @BitmapImageOnClick;
  CommonNameInfoBitmapImage.Hide;

  LoadSilentParameters;

  // apply the change to ui
  if not IsSilentMode then
  begin
    OnServiceChange(AgentInstanceComboBox);
  end;
  
end;

// Keep a taller height only while the ConfigPage is active.
procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = ConfigPage.ID then
    WizardForm.ClientHeight := 600
  else
    WizardForm.ClientHeight := 570;
end;

{ ────────────────────────────────────────────────────────────────────── }
{ ──────────────────────────── Handler Wizard ────────────────────────── }
{ ────────────────────────────────────────────────────────────────────── }
// Handles the logic for the Next button click event in the installer wizard.
// Validates user input and required fields for each wizard page.
// Displays error messages and prevents continuation on invalid input.
// Manages plugin download and registry updates as needed.
function NextButtonClick(CurPageID: Integer): Boolean;
begin
  if CurPageID = wpReady then begin
    WriteAgentRegistryValues;
    RestartService;
    Result := True;
  end else if CurPageID = ConfigPage.ID then begin
    // disable all warnings
    HostWarningBitmapImage.Hide;
    PollerWarningBitmapImage.Hide;
    LogFileWarningBitmapImage.Hide;
    
    if HostNameEdit.Text = '' then begin
      HostWarningBitmapImage.Hint := 'Please select at least one component before continuing.';
      HostWarningBitmapImage.Show;
      MsgBox('Please enter the host name. This field cannot be left empty.', mbError, MB_OK);
      Result := False;
      exit;
    end;

    if not IsValidHostPortFormat(PollerEndpointEdit.Text) then begin
      Result := False;
      exit;
    end;

    if (LogTypeComboBox.Text = 'File') and (LogFileEdit.Text = '') then begin
      LogFileWarningBitmapImage.Hint := 'Please select a log file before continuing.';
      LogFileWarningBitmapImage.Show;
      MsgBox('Please enter the log file path. This field cannot be left empty.', mbError, MB_OK);
      Result := False;
      exit;
    end;

    if (LogTypeComboBox.Text = 'File') and not(IsPathValid(LogFileEdit.Text)) then 
      begin
        LogFileWarningBitmapImage.Hint := 'Invalid log file path. Please enter a valid absolute path (e.g. C:\logs\agent.log).';
        LogFileWarningBitmapImage.Show;
        MsgBox('Invalid log file path. Please enter a valid absolute path (e.g. C:\logs\agent.log).', mbError, MB_OK);
        Result := False;
        exit;
      end;

    if PollerInitiatedCheckBox.Checked then
    begin
      if EncryptionComboBox.Items.IndexOf('Insecure TLS') <> -1 then
        EncryptionComboBox.Items.Delete(EncryptionComboBox.Items.IndexOf('Insecure TLS'));
    end
    else
    begin
      if EncryptionComboBox.Items.IndexOf('Insecure TLS') = -1 then
        EncryptionComboBox.Items.Insert(1, 'Insecure TLS');
    end;

    // apply the change to ui
    OnEncryptionChange(EncryptionComboBox);
    Result := True;
  end 
  else if CurPageID = EncryptionPage.ID then begin
    PublicCertWarningBitmapImage.Hide;
    PrivateKeyWarningBitmapImage.Hide;
    CaWarningBitmapImage.Hide;
    TokenWarningBitmapImage.Hide;
    if EncryptionComboBox.Text <> 'No TLS' then begin
      if PollerInitiatedCheckBox.Checked then begin
        if not(IsPathValid(PublicCertEdit.Text)) then begin
          PublicCertWarningBitmapImage.Hint := 'Invalid public certificate file path. Please enter a valid absolute path (e.g. C:\certs\public.crt).';
          PublicCertWarningBitmapImage.Show;
          MsgBox('Invalid public certificate file path. Please enter a valid absolute path (e.g. C:\certs\public.crt).Allowed char 0..9 , A..Z , \ , . , - , ( , ) ', mbError, MB_OK);
          Result := False;
          exit;
        end;

        if not(IsPathValid(PrivateKeyEdit.Text)) then begin
          PrivateKeyWarningBitmapImage.Hint := 'Invalid private key file path. Please enter a valid absolute path (e.g. C:\certs\private.key).';
          PrivateKeyWarningBitmapImage.Show;
          MsgBox('Invalid private key file path. Please enter a valid absolute path (e.g. C:\certs\private.key).Allowed char 0..9 , A..Z , \ , . , - , ( , ) ', mbError, MB_OK);
          Result := False;
          exit;
        end;
      end
      else begin
        if not(IsPathValid(CaEdit.Text)) and (CaEdit.Text <> '') then begin
          CaWarningBitmapImage.Hint := 'Invalid CA certificate file path. Please enter a valid absolute path (e.g. C:\certs\ca.crt).';
          CaWarningBitmapImage.Show;
          MsgBox('Invalid CA certificate file path. Please enter a valid absolute path (e.g. C:\certs\ca.crt).Allowed char 0..9 , A..Z , \ , . , - , ( , ) ', mbError, MB_OK);
          Result := False;
          exit;
        end;
      end;
    end;
    if TokenEdit.Text = '' then begin
        TokenWarningBitmapImage.Hint := 'Please enter Token (JWT). This field cannot be left empty.';
        TokenWarningBitmapImage.Show;
        MsgBox('Please enter Token (JWT). This field cannot be left empty.', mbError, MB_OK);
        Result := False;
        exit;
    end;
    Result := True;
  end
  else
    Result := True;
end;