unit TwitchAPI.Client;

interface

uses
  System.Generics.Collections,
  System.Net.HttpClient,
  System.JSON,
  System.SysUtils;

type

  TTwitchClient = class;

{ TTwitchResult }

  TTwitchResult = class abstract (TInterfacedObject)
  public
    procedure Parse(const AData: string); virtual; abstract;
  end;

{ TTwitchCommand }

  TTwitchCommand = class abstract (TInterfacedObject)
  private
    FBody: TJSONObject;
    FClient: TTwitchClient;
    FQuery: TDictionary<string, string>;
  protected
    function Execute<TResult: TTwitchResult, constructor>: TResult;
  public
    constructor Create(AClient: TTwitchClient); virtual;
    destructor Destroy; override;
    function GetEndpoint: string; virtual;
    property Body: TJSONObject read FBody;
    property Query: TDictionary<string, string> read FQuery;
  end;

{ TTwitchClient }

  TTwitchClient = class
  private
    FBaseUrl: string;
    FClientID: string;
    FHTTP: THTTPClient;
    FToken: string;
  public
    constructor Create(const AClientID: string = ''; const AToken: string = '');
    destructor Destroy; override;
    procedure Get(const AEndpoint: string; AQueryParams: TDictionary<string, string>; AResult: TTwitchResult);
    property BaseUrl: string read FBaseUrl write FBaseUrl;
    property ClientID: string read FClientID write FClientID;
    property HTTP: THTTPClient read FHTTP;
    property Token: string read FToken write FToken;
  end;

implementation

uses
  System.Classes,
  System.Net.URLClient;

const
  DEFAULT_BASE_URL = 'https://api.twitch.tv/helix';

{ TTwitchCommand<TResult> }

constructor TTwitchCommand.Create(AClient: TTwitchClient);
begin
  inherited Create;
  FBody := TJSONObject.Create;
  FClient := AClient;
  FQuery := TDictionary<string, string>.Create;
end;

destructor TTwitchCommand.Destroy;
begin
  if Assigned(FBody) then
    FreeAndNil(FBody);
  if Assigned(FQuery) then
    FreeAndNil(FQuery);
  inherited Destroy;
end;

function TTwitchCommand.Execute<TResult>: TResult;
begin
  Result := TResult.Create;
  try
    FClient.Get(GetEndpoint, FQuery, Result);
  except
    FreeAndNil(Result);
    raise;
  end;
end;

function TTwitchCommand.GetEndpoint: string;
begin
  Result := '/';
end;

{ TTwitchClient }

constructor TTwitchClient.Create(const AClientID, AToken: string);
begin
  inherited Create;
  FBaseUrl := DEFAULT_BASE_URL;
  FClientID := AClientID;
  FHTTP := THTTPClient.Create;
  FToken := AToken;
end;

destructor TTwitchClient.Destroy;
begin
  if Assigned(FHTTP) then
    FreeAndNil(FHTTP);
  inherited Destroy;
end;

procedure TTwitchClient.Get(const AEndpoint: string;
  AQueryParams: TDictionary<string, string>;
  AResult: TTwitchResult);
begin
  var LURI := TURI.Create(FBaseUrl + AEndpoint);
  for var LPair in AQueryParams do
    LURI.AddParameter(LPair.Key, LPair.Value);
  var LHeaders: TNetHeaders := [
    TNetHeader.Create('Authorization', Format('Bearer %s', [FToken])),
    TNetHeader.Create('Client-Id', FClientID)
  ];
  var LStream := TStringStream.Create;
  try
    FHTTP.Get(LURI.ToString, LStream, LHeaders);
    AResult.Parse(LStream.DataString);
  finally
    LStream.Free;
  end;
end;

end.
