unit uAPIDA;

interface

uses
  Classes, SysUtils, System.JSON, IdHTTP, IdSSLOpenSSL, flog;

type
  TAPIClient = class(TObject)
  private
    FHttpClient: TIdHTTP;
    FSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
    FToken: string;
    procedure SetToken(const Value: string);
    procedure CheckHTTPError(AResponseCode: Integer; const AResponse: string);
  public
    constructor Create;
    destructor Destroy; override;
    function GetAccessToken(const client_id, client_secret, redirect_uri, code: string): string;
    function GetUserInfo: TJSONObject;
    function SubscribeToChannel(const uid, clientUID: string): TJSONObject;
    property Token: string read FToken write SetToken;
  end;

implementation

const
  ContentType = 'application/x-www-form-urlencoded';
  UserAgent = 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
  AuthorizationHeader = 'Authorization: Bearer ';

constructor TAPIClient.Create;
begin
  inherited;
  FHttpClient := TIdHTTP.Create(nil);
  // создаём SSL handler без владельца — явное управление
  FSSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  FSSLHandler.SSLOptions.Method := sslvSSLv23;
  FHttpClient.IOHandler := FSSLHandler;
  FHttpClient.Request.UserAgent := UserAgent;
  FHttpClient.Request.ContentType := ContentType;
  FHttpClient.HandleRedirects := True;
end;

destructor TAPIClient.Destroy;
begin
  // Отключаем и освобождаем в безопасном порядке
  try
    if Assigned(FHttpClient) then
    begin
      try
        // если нужно — прервать активные соединения
      except
      end;
    end;
  finally
    // Сначала освобождаем IOHandler (если он не принадлежит FHttpClient)
    FreeAndNil(FSSLHandler);
    FreeAndNil(FHttpClient);
    inherited;
  end;
end;

procedure TAPIClient.CheckHTTPError(AResponseCode: Integer; const AResponse: string);
begin
  if AResponseCode <> 200 then
    raise Exception.CreateFmt('HTTP Error %d: %s', [AResponseCode, AResponse]);
end;

function TAPIClient.GetAccessToken(const client_id, client_secret, redirect_uri, code: string): string;
var
  Response: string;
  Stream: TStringStream;
  Json: TJSONObject;
begin
  Stream := TStringStream.Create(
    Format('grant_type=authorization_code&client_id=%s&client_secret=%s&redirect_uri=%s&code=%s',
    [client_id, client_secret, redirect_uri, code]), TEncoding.UTF8);
  try
    Response := FHttpClient.Post('https://www.donationalerts.com/oauth/token', Stream);
    CheckHTTPError(FHttpClient.ResponseCode, Response);

    Json := TJSONObject.ParseJSONValue(Response) as TJSONObject;
    try
      Result := Json.GetValue<string>('access_token');
      FToken := Result;
    finally
      Json.Free;
    end;
  finally
    Stream.Free;
  end;
end;

function TAPIClient.GetUserInfo: TJSONObject;
var
  Response: string;
begin
  FHttpClient.Request.CustomHeaders.Add(AuthorizationHeader + FToken);
  try
    Response := FHttpClient.Get('https://www.donationalerts.com/api/v1/user/oauth');
    CheckHTTPError(FHttpClient.ResponseCode, Response);
    Result := TJSONObject.ParseJSONValue(Response) as TJSONObject;
  finally
    FHttpClient.Request.CustomHeaders.Clear;
  end;
end;

function TAPIClient.SubscribeToChannel(const uid, clientUID: string): TJSONObject;
var
  Response: string;
  Stream: TStringStream;
  RequestJSON: string;
begin
  RequestJSON := Format('{"channels":["$alerts:donation_%s"], "client":"%s"}', [uid, clientUID]);
  Stream := TStringStream.Create(RequestJSON, TEncoding.UTF8);
  try
    FHttpClient.Request.CustomHeaders.Add(AuthorizationHeader + FToken);
    FHttpClient.Request.ContentType := 'application/json';
    Response := FHttpClient.Post('https://www.donationalerts.com/api/v1/centrifuge/subscribe', Stream);
    CheckHTTPError(FHttpClient.ResponseCode, Response);
    Result := TJSONObject.ParseJSONValue(Response) as TJSONObject;
  finally
    Stream.Free;
    FHttpClient.Request.CustomHeaders.Clear;
  end;
end;

procedure TAPIClient.SetToken(const Value: string);
begin
  FToken := Value;
end;

end.

