//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "AdvGrid"
#pragma link "AdvObj"
#pragma link "AdvUtil"
#pragma link "BaseGrid"
#pragma link "DBAdvGrid"
#pragma link "AdvSplitter"
#pragma link "AdvEdit"
#pragma link "vcl.wwdbcomb"
#pragma link "vcl.wwdbedit"
#pragma link "vcl.wwdotdot"
#pragma link "AdvOfficePager"
#pragma link "MemDS"
#pragma link "VirtualTable"
#pragma link "DBAccess"
#pragma link "Uni"
#pragma link "vcl.wwdbdatetimepicker"
#pragma link "PostgreSQLUniProvider"
#pragma link "UniProvider"
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  pagesLoaded = 0;

  accountSid = "AC717f06b3c10bbecff42487426e34fdb3";
  authHeader = "Basic QUM3MTdmMDZiM2MxMGJiZWNmZjQyNDg3NDI2ZTM0ZmRiMzphMGRlYzEyOGMxYjVhMzIzMTkwOWJhODFkYTg5NDdiNg==";

  TDateTime dtNow = Date();
  unsigned short y, m, d;
  dtNow.DecodeDate(&y, &m, &d);

  for(int i = 0; i < 12; i++)
	dbcbYear->Items->Add(y - i);

  static String sMonths[12] = {
    "January","February","March","April","May","June",
    "July","August","September","October","November","December"
  };
  for(int i = 0; i < 12; i++)
	dbcbMonth->Items->Add(sMonths[i]);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
  Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnMessagesClick(TObject *Sender)
{
  String res = BuildMessageRequestURL();
  TJSONValue* jValue;
  TJSONObject* jObj;
  TJSONArray* jaMessages;
  String jsonPair;

  Memo1->Lines->Add("---------------------------------------------------------------");
  Memo1->Lines->Add("/Messages.json end point...");
  Memo2->Clear();
  Memo3->Clear();

  // Create REST client
  TRESTClient* pRESTClient = new TRESTClient(NULL);
  pRESTClient->BaseURL = "https://api.twilio.com";

  TRESTRequest* pRESTRequest = new TRESTRequest(NULL);
  pRESTRequest->Client = pRESTClient;

  TRESTResponse* pRESTResponse = new TRESTResponse(NULL);
  pRESTRequest->Response = pRESTResponse;

  pRESTRequest->Method = rmGET;
  pRESTRequest->Resource = res;

  // Set authorization header
  TRESTRequestParameter* param = pRESTRequest->Params->AddItem();
  param->Name = "Authorization";
  param->Kind = pkHTTPHEADER;
  param->Options = TRESTRequestParameterOptions() << poDoNotEncode;
  param->Value = authHeader;

  Memo1->Lines->Add("Messages: " + pRESTRequest->GetFullRequestURL());
  pRESTRequest->Execute();

  jValue = pRESTResponse->JSONValue;
  Memo3->Lines->Add("");
  Memo3->Lines->Add("jValue = pRESTResponse->JSONValue;");
  Memo3->Lines->Add("---------------------------------------------------------------------");
  Memo3->Lines->Add(jValue->Format(2));

  jObj = dynamic_cast<TJSONObject*>(jValue);

  if (jObj)
  {
    for (int e = 0; e < jObj->Count; e++)
	{
      if (jObj->Pairs[e]->JsonString->Value() != "messages")
      {
        jsonPair = jObj->Pairs[e]->JsonString->Value();
        jsonPair += " : " + jObj->Pairs[e]->JsonValue->Value();
        Memo1->Lines->Add(jsonPair);
      }
    }

	if (jObj->Get("next_page_uri"))
      txtNextPageUri->Text = jObj->Get("next_page_uri")->JsonValue->Value();
    else
      txtNextPageUri->Text = "";

    jaMessages = dynamic_cast<TJSONArray*>(jObj->GetValue("messages"));
    if (jaMessages && jaMessages->Count > 0)
    {
      Memo2->Lines->Add("jaMessages->Count: " + IntToStr(jaMessages->Count));

      GetFieldsFromJsonArray(jaMessages);
      for (int i = 0; i < fieldsList->Count; i++)
      {
        Memo2->Lines->Add(fieldsList->Strings[i]);
      }
      LoadJsonArray(jaMessages, "");
      Memo2->Lines->Add("grdMessages->Count: " + IntToStr(grdData->RowCount - 1));

	  pagesLoaded = 1;
    }
    else
    {
      Memo2->Lines->Add("No messages returned.");
      grdData->ClearAll();
      grdData->RowCount = 1;
    }
  }
  else
  {
    Memo1->Lines->Add("No JSON object returned.");
  }

  if (cbSaveToDB->Checked)
  {
	SaveMessagesToDB(jaMessages);
  }

  delete pRESTClient;
  delete pRESTRequest;
  delete pRESTResponse;
}
//---------------------------------------------------------------------------
void TForm1::SaveMessagesToDB(TJSONArray* jaMessages)
{
  if (!jaMessages || jaMessages->Count == 0)
    return;

  if (!ucTwilioDB->Connected)
    ucTwilioDB->Connected = true;

  if (!uqMessages->Active)
    uqMessages->Active = true;

  for (int i = 0; i < jaMessages->Count; i++)
  {
    TJSONObject* jso = dynamic_cast<TJSONObject*>(jaMessages->Items[i]);
    if (!jso)
      continue;

    String sid = jso->GetValue("sid")->Value();

    // Check if the message already exists
	uqMessages->Close();
	uqMessages->SQL->Text = "SELECT * FROM public.messages WHERE sid = :sid";
	uqMessages->ParamByName("sid")->AsString = sid;
	Memo1->Lines->Add("Checking available fields in uqMessages:");
	uqMessages->Open();

	if (uqMessages->IsEmpty())
	{
	  uqMessages->Append();  // Insert new record
	}
	else
	{
	  uqMessages->Edit();    // Update existing record
	}
	uqMessages->FieldByName("sid")->AsString = sid;
	uqMessages->FieldByName("account_sid")->AsString = jso->GetValue("account_sid")->Value();
    uqMessages->FieldByName("api_version")->AsString = jso->GetValue("api_version")->Value();
	uqMessages->FieldByName("body")->AsString = jso->GetValue("body")->Value();
	uqMessages->FieldByName("date_created")->AsDateTime = ParseTwilioDateTime(jso->GetValue("date_created")->Value());
	uqMessages->FieldByName("date_sent")->AsDateTime = ParseTwilioDateTime(jso->GetValue("date_sent")->Value());
	uqMessages->FieldByName("date_updated")->AsDateTime = ParseTwilioDateTime(jso->GetValue("date_updated")->Value());
    uqMessages->FieldByName("direction")->AsString = jso->GetValue("direction")->Value();
    uqMessages->FieldByName("error_code")->AsInteger = jso->GetValue("error_code")->Value().ToIntDef(0);
	uqMessages->FieldByName("error_message")->AsString = jso->GetValue("error_message")->Value();
    uqMessages->FieldByName("from_number")->AsString = jso->GetValue("from")->Value();
	uqMessages->FieldByName("messaging_service_sid")->AsString = jso->GetValue("messaging_service_sid")->Value();
	uqMessages->FieldByName("num_media")->AsInteger = jso->GetValue("num_media")->Value().ToIntDef(0);
    uqMessages->FieldByName("num_segments")->AsInteger = jso->GetValue("num_segments")->Value().ToIntDef(0);
	uqMessages->FieldByName("price")->AsFloat = jso->GetValue("price")->Value().ToDouble();
	uqMessages->FieldByName("price_unit")->AsString = jso->GetValue("price_unit")->Value();
	uqMessages->FieldByName("status")->AsString = jso->GetValue("status")->Value();
	uqMessages->FieldByName("to_number")->AsString = jso->GetValue("to")->Value();
	uqMessages->FieldByName("uri")->AsString = jso->GetValue("uri")->Value();
	uqMessages->Post();
  }
}
//---------------------------------------------------------------------------
TDateTime TForm1::ParseTwilioDateTime(const UnicodeString &dateStr)
{
	// Twilio format: "Wed, 12 Feb 2025 18:21:17 +0000"
	std::map<UnicodeString, int> monthMap;
    monthMap["Jan"] = 1; monthMap["Feb"] = 2; monthMap["Mar"] = 3;
	monthMap["Apr"] = 4; monthMap["May"] = 5; monthMap["Jun"] = 6;
	monthMap["Jul"] = 7; monthMap["Aug"] = 8; monthMap["Sep"] = 9;
	monthMap["Oct"] = 10; monthMap["Nov"] = 11; monthMap["Dec"] = 12;

	UnicodeString cleanedDateStr = dateStr.SubString(6, dateStr.Length() - 5);

	cleanedDateStr = cleanedDateStr.SubString(1, cleanedDateStr.Length() - 6);

    TStringList *parts = new TStringList();
    parts->Delimiter = ' ';
    parts->StrictDelimiter = true;
    parts->DelimitedText = cleanedDateStr;

    if (parts->Count < 4) {
        delete parts;
        throw EConvertError("Invalid Twilio date format: " + dateStr);
    }

    int day = parts->Strings[0].ToInt();
    UnicodeString monthStr = parts->Strings[1];
    int year = parts->Strings[2].ToInt();
    UnicodeString timeStr = parts->Strings[3];

    if (monthMap.find(monthStr) == monthMap.end()) {
        delete parts;
        throw EConvertError("Invalid month name: " + monthStr);
    }
    int month = monthMap[monthStr];

    UnicodeString formattedDate = UnicodeString::Format(
        L"%04d-%02d-%02d %s", ARRAYOFCONST((year, month, day, timeStr))
    );

	TFormatSettings formatSettings;
	GetLocaleFormatSettings(0, formatSettings);
    formatSettings.ShortDateFormat = "yyyy-mm-dd";
    formatSettings.LongTimeFormat = "hh:nn:ss";
    formatSettings.DateSeparator = '-';
    formatSettings.TimeSeparator = ':';

    TDateTime result = StrToDateTime(formattedDate, formatSettings);

    delete parts;
    return result;
}
//---------------------------------------------------------------------------
String TForm1::BuildMessageRequestURL()
{
  String url = "/2010-04-01/Accounts/" + accountSid + "/Messages.json";
  String pageSizeStr = edtPageSize->Text.Trim();

  bool hasStartDate = !dtpStartDate->Text.IsEmpty();
  bool hasEndDate = !dtpEndDate->Text.IsEmpty();

  if (hasStartDate || hasEndDate)
  {
    TDateTime d1, d2;
    if (hasStartDate) d1 = dtpStartDate->Date;
    if (hasEndDate) d2 = dtpEndDate->Date;

    if (hasStartDate && hasEndDate)
    {
      if (d1 > d2) std::swap(d1, d2);
	  url += "?DateSent>=" + FormatDateTime("yyyy-mm-dd", d1) +
             "&DateSent<=" + FormatDateTime("yyyy-mm-dd", d2);
    }
    else if (hasStartDate)
    {
      url += "?DateSent>=" + FormatDateTime("yyyy-mm-dd", d1);
    }
    else if (hasEndDate)
    {
	  url += "?DateSent<=" + FormatDateTime("yyyy-mm-dd", d2);
    }

    if (!pageSizeStr.IsEmpty())
      url += "&PageSize=" + pageSizeStr;
  }
  else
  {
    if (!pageSizeStr.IsEmpty())
	  url += "?PageSize=" + pageSizeStr;
  }

  return url;
}
//---------------------------------------------------------------------------
void TForm1::GetFieldsFromJsonArray(TJSONArray* jaData)
{
  String fieldname;
  String str;
  TJSONObject* jso;
  fieldsList = new TStringList;

  if(!jaData || jaData->Count == 0)
    return;

  jso = dynamic_cast<TJSONObject*>(jaData->Items[0]);
  if(!jso)
	return;

  for( int i = 0; i < jso->Count; i++ ){
    fieldname = jso->Pairs[i]->JsonString->Value();
    str = fieldname + "=0";
    fieldsList->Add( str );
  }
}
//---------------------------------------------------------------------------
void TForm1::LoadJsonArray(TJSONArray* jaData, String detail)
{
  TJSONObject* jso;
  grdData->ClearAll();
  grdData->RowCount = 1;
  grdData->StartUpdate();

  if(!jaData || jaData->Count == 0)
  {
	grdData->EndUpdate();
    return;
  }

  jso = dynamic_cast<TJSONObject*>(jaData->Items[0]);
  if(!jso)
  {
    grdData->EndUpdate();
    return;
  }

  grdData->ColCount = jso->Count;
  for( int e = 0; e < jso->Count; e++ )
    grdData->Cells[e+1][0] = jso->Pairs[e]->JsonString->Value();

  for( int i = 0; i < jaData->Count; i++ ){
    jso = dynamic_cast<TJSONObject*>(jaData->Items[i]);
    if(!jso)
	  continue;
    grdData->RowCount++;
    int row = grdData->RowCount - 1;
    for( int e = 0; e < jso->Count; e++ )
      grdData->Cells[e+1][row] = jso->Pairs[e]->JsonValue->Value();
  }
  grdData->EndUpdate();
}
//---------------------------------------------------------------------------
void TForm1::AppendJsonArray(TJSONArray* jaData)
{
  if(!jaData || jaData->Count == 0)
    return;

  grdData->BeginUpdate();
  for(int i = 0; i < jaData->Count; i++)
  {
	TJSONObject* jso = dynamic_cast<TJSONObject*>(jaData->Items[i]);
    if(!jso)
      continue;

    grdData->RowCount++;
    int row = grdData->RowCount - 1;
    for(int e = 0; e < jso->Count; e++)
    {
      grdData->Cells[e+1][row] = jso->Pairs[e]->JsonValue->Value();
	}
  }
  grdData->EndUpdate();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::dbcbMonthCloseUp(TwwDBComboBox *Sender, bool Select)
{
  SetDatePickersFromMonthYear();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::dbcbYearCloseUp(TwwDBComboBox *Sender, bool Select)
{
  SetDatePickersFromMonthYear();
}
//---------------------------------------------------------------------------
void TForm1::SetDatePickersFromMonthYear()
{
  int year = 0;
  int month = 0;

  if(!dbcbYear->Value.IsEmpty())
    year = StrToInt(dbcbYear->Value);

  if(!dbcbMonth->Value.IsEmpty())
  {
    String selMonth = dbcbMonth->Value;
    if(selMonth == "January")   month = 1;
	else if(selMonth == "February")  month = 2;
    else if(selMonth == "March")     month = 3;
    else if(selMonth == "April")     month = 4;
    else if(selMonth == "May")       month = 5;
    else if(selMonth == "June")      month = 6;
    else if(selMonth == "July")      month = 7;
    else if(selMonth == "August")    month = 8;
    else if(selMonth == "September") month = 9;
    else if(selMonth == "October")   month = 10;
	else if(selMonth == "November")  month = 11;
    else if(selMonth == "December")  month = 12;
  }

  if(month > 0 && year > 0)
  {
    TDateTime s = StartOfAMonth(year, month);
    TDateTime e = EndOfAMonth(year, month);
    dtpStartDate->Date = s;
	dtpEndDate->Date   = e;
  }
  else if(year > 0)
  {
    TDateTime s = StartOfAMonth(year, 1);
    TDateTime ee = EndOfAMonth(year, 12);
    dtpStartDate->Date = s;
    dtpEndDate->Date   = ee;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnNextPageClick(TObject *Sender)
{
  // Stop if there's no next page URI
  if (txtNextPageUri->Text.Trim().IsEmpty() || txtNextPageUri->Text == "null")
  {
    Memo2->Lines->Add("No more pages left to load.");
    return;
  }

  TRESTClient* pRESTClient = new TRESTClient(NULL);
  pRESTClient->BaseURL = "https://api.twilio.com";

  TRESTRequest* pRESTRequest = new TRESTRequest(NULL);
  pRESTRequest->Client = pRESTClient;

  TRESTResponse* pRESTResponse = new TRESTResponse(NULL);
  pRESTRequest->Response = pRESTResponse;

  pRESTRequest->Method = rmGET;
  pRESTRequest->Resource = txtNextPageUri->Text;

  TRESTRequestParameter* param = pRESTRequest->Params->AddItem();
  param->Name = "Authorization";
  param->Kind = pkHTTPHEADER;
  param->Options = TRESTRequestParameterOptions() << poDoNotEncode;
  param->Value = authHeader;

  pRESTRequest->Execute();

  TJSONObject* jObj = dynamic_cast<TJSONObject*>(pRESTResponse->JSONValue);
  if (!jObj)
  {
    Memo2->Lines->Add("No JSON object returned for next page.");
  }
  else
  {
	TJSONArray* jaMessages = dynamic_cast<TJSONArray*>(jObj->GetValue("messages"));
    if (jaMessages && jaMessages->Count > 0)
	{
	  AppendJsonArray(jaMessages);  // Append new messages
	  pagesLoaded++;
      Memo2->Lines->Add("Loaded next page (#" + IntToStr(pagesLoaded) + ").");
	  Memo2->Lines->Add("Total messages in grid: " + IntToStr(grdData->RowCount - 1));

      // Update next_page_uri
	  if (jObj->Get("next_page_uri"))
		txtNextPageUri->Text = jObj->Get("next_page_uri")->JsonValue->Value();
      else
        txtNextPageUri->Text = "";  // Stop further pagination
	}
    else
	{
      Memo2->Lines->Add("No messages in next page.");
	}
  }

  delete pRESTClient;
  delete pRESTRequest;
  delete pRESTResponse;
}

//---------------------------------------------------------------------------

