unit uDataBase;

interface

uses
  System.SysUtils, System.Classes, FireDAC.Comp.Client, FireDAC.Stan.Param,
  FMX.Grid, FireDAC.Stan.Def, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Pool,
  FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.SQLite, FireDAC.Phys.SQLiteDef,
  FireDAC.Stan.ExprFuncs, FireDAC.Phys.SQLiteWrapper.Stat, FireDAC.VCLUI.Wait,
  FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt, Data.DB, FireDAC.Comp.DataSet,
  FireDAC.FMXUI.Wait, FireDAC.Comp.UI, uRecords, System.Generics.Collections;

type
  TSettingsDatabase = class

  private
    FConnection: TFDConnection;
    procedure InitializeDatabase;
    function GetSetting(const Name: string): string;
    procedure SetSetting(const Name, Value: string);
    function GetColumnsDefinition(Grid: TStringGrid): string;
    function GetColumnsList(Grid: TStringGrid): string;
    function GetValuesPlaceholders(Grid: TStringGrid): string;
    function CheckTableExists(const TableName: string): Boolean;
  public
    FChannel: string;

    constructor Create(const DatabasePath: string);
    destructor Destroy; override;

    // Методы для работы с настройками
    function ReadSetting(const Name: string; Default: string = ''): string;
    procedure WriteSetting(const Name, Value: string);
    function getLoginData(): TLogin;

    // Методы для работы с TStringGrid
    procedure SaveGridToTable(const TableName: string; Grid: TStringGrid);
    procedure LoadGridFromTable(const TableName: string; Grid: TStringGrid);

    // Методы для работы с Users
    // procedure SaveUsers(const Users: array of User);
    procedure LoadUsers(var users: tlist<tuser>);

    // Методы для работы с Групповыми ответами
    procedure addGroupResponse(Name, Respons: string);
    procedure getGroupResponse(aName: string; const lbResponse: Tstrings);
    procedure getGroupName(const lbName: Tstrings);
    procedure delGroupName(aName: string);
    procedure delGroupResponse(aName, aResponse: string);

  end;

implementation

uses uGeneral;

constructor TSettingsDatabase.Create(const DatabasePath: string);
begin
  FConnection := TFDConnection.Create(nil);
  try
    FConnection.DriverName := 'SQLite';
    FConnection.Params.Database := DatabasePath;
    FConnection.Connected := True;
    InitializeDatabase;
  except
    on E: Exception do
    begin
      FreeAndNil(FConnection);
      raise Exception.Create('Ошибка при подключении к базе данных: ' +
        E.Message);
    end;
  end;
end;

procedure TSettingsDatabase.delGroupName(aName: string);
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    Query.SQL.Text := 'delete FROM GroupResponse where Name = "' + aName + '"';
    Query.ExecSQL;
  finally
    Query.Free;
  end;
end;

procedure TSettingsDatabase.delGroupResponse(aName, aResponse: string);
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    Query.SQL.Text := 'delete FROM GroupResponse where Name = :name' +
      ' and Response = :response';
    Query.Params.ParamByName('name').AsString := aName;
    Query.Params.ParamByName('response').AsString := aResponse;
    Query.ExecSQL;
  finally
    Query.Free;
  end;
end;

destructor TSettingsDatabase.Destroy;
begin
  if Assigned(FConnection) then
    FConnection.Connected := False;
  FreeAndNil(FConnection);
  inherited;
end;

procedure TSettingsDatabase.InitializeDatabase;
var
  FDQuery: TFDQuery;
  FieldExists: Boolean;
begin
  // Создаем таблицу, если она не существует
  FConnection.ExecSQL('CREATE TABLE IF NOT EXISTS params (' +
    '  name TEXT PRIMARY KEY,' + '  value TEXT' + ');');
  // Создаем таблицу для пользователей, если она не существует
  FConnection.ExecSQL('CREATE TABLE IF NOT EXISTS users (' +
    '  id TEXT PRIMARY KEY,' + '  login TEXT,' + '  DisplayName TEXT,' +
    '  created_at DATETIME,' + '  follow_at DATETIME,' + '  isVip TEXT,' +
    '  isModer TEXT,' + '  isO TEXT,' + '  streamer TEXT' +

    ');');
  FDQuery := TFDQuery.Create(nil);
  try
    FDQuery.Connection := FConnection;
    FieldExists := False;
    FDQuery.SQL.Text := 'PRAGMA table_info(users)';
    FDQuery.Open;
    while not FDQuery.EOF do
    begin
      if FDQuery.FieldByName('name').AsString = 'streamer' then
      begin
        FieldExists := True;
        Break;
      end;
      FDQuery.Next;
    end;
    FDQuery.Close;
    if not FieldExists then
    begin
      FConnection.ExecSQL('ALTER TABLE users ADD COLUMN streamer TEXT');
    end;
  finally
    FDQuery.Free;
  end;

  FConnection.ExecSQL
    ('CREATE TABLE IF NOT EXISTS GroupResponse (ID INTEGER PRIMARY KEY, Name TEXT, Response TEXT);');

end;

{ procedure TSettingsDatabase.SaveUsers(const Users: array of User);
  var
  Query: TFDQuery;
  UserItem: User;
  s: string;
  begin
  if Length(Users) < 1 then
  exit;
  // Вставляем данные из массива пользователей в таблицу
  Query := TFDQuery.Create(nil);
  try
  Query.Connection := FConnection;
  for UserItem in Users do
  begin
  Query.SQL.Text :=
  'INSERT OR REPLACE INTO users (id, login, DisplayName, created_at, follow_at, isVip, isModer, isO, streamer) '
  + 'VALUES (:id, :login, :DisplayName, :created_at, :follow_at, :isVip, :isModer, :isO, :streamer);';
  Query.ParamByName('id').AsString := UserItem.id;
  Query.ParamByName('login').AsString := UserItem.login;
  Query.ParamByName('DisplayName').AsString := UserItem.DisplayName;
  Query.ParamByName('created_at').AsDateTime := UserItem.created_at;
  Query.ParamByName('follow_at').AsDateTime := UserItem.follow_at;
  Query.ParamByName('streamer').AsString := FChannel;

  if UserItem.isVip then
  s := 'True'
  else
  s := 'False';
  Query.ParamByName('isVip').AsString := s;
  if UserItem.isModer then
  s := 'True'
  else
  s := 'False';
  Query.ParamByName('isModer').AsString := s;
  if UserItem.isO then
  s := 'True'
  else
  s := 'False';
  Query.ParamByName('isO').AsString := s;
  Query.ExecSQL;
  end;
  finally
  Query.Free;
  end;
  end; }

procedure TSettingsDatabase.LoadUsers(var users: tlist<tuser>);
var
  Query: TFDQuery;
  UserItem: tuser;

begin

  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    Query.SQL.Text := 'SELECT * FROM users WHERE streamer = :streamer';
    Query.ParamByName('streamer').AsString := FChannel;
    Query.Open;

    while not Query.EOF do
    begin
      UserItem.id := Query.FieldByName('id').AsString;
      UserItem.login := Query.FieldByName('login').AsString;
      UserItem.DisplayName := Query.FieldByName('DisplayName').AsString;
      UserItem.created_at := Query.FieldByName('created_at').AsDateTime;
      UserItem.follow_at := Query.FieldByName('follow_at').AsDateTime;
      UserItem.isVip := Query.FieldByName('isVip').AsString = 'True';
      UserItem.isModer := Query.FieldByName('isModer').AsString = 'True';
      UserItem.isO := Query.FieldByName('isO').AsString = 'True';
      UserItem.isO_today := False;
      users.Add(UserItem);
      Query.Next;
    end;

  finally
    Query.Free;
  end;
end;

function TSettingsDatabase.GetColumnsDefinition(Grid: TStringGrid): string;
var
  Col: Integer;
begin
  Result := '';
  for Col := 0 to Grid.ColumnCount - 1 do
  begin
    if Result <> '' then
      Result := Result + ', ';
    Result := Result + 'col' + IntToStr(Col) + ' TEXT';
  end;
end;

function TSettingsDatabase.GetColumnsList(Grid: TStringGrid): string;
var
  Col: Integer;
begin
  Result := '';
  for Col := 0 to Grid.ColumnCount - 1 do
  begin
    if Result <> '' then
      Result := Result + ', ';
    Result := Result + 'col' + IntToStr(Col);
  end;
end;

procedure TSettingsDatabase.getGroupName(const lbName: Tstrings);
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;

    lbName.Clear;
    Query.SQL.Text := 'SELECT DISTINCT Name FROM GroupResponse';
    Query.Open;
    while not Query.EOF do
    begin
      lbName.Add(Query.FieldByName('Name').AsString);
      Query.Next;
    end;
  finally
    Query.Free;
  end;
end;

procedure TSettingsDatabase.getGroupResponse(aName: string;
  const lbResponse: Tstrings);
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;

    lbResponse.Clear;
    Query.SQL.Text := 'SELECT * FROM GroupResponse WHERE  name=:TableName';
    Query.ParamByName('TableName').AsString := aName;
    Query.Open;
    while not Query.EOF do
    begin
      lbResponse.Add(Query.FieldByName('Response').AsString);
      Query.Next;
    end;
  finally
    Query.Free;
  end;

end;

function TSettingsDatabase.getLoginData: TLogin;
begin
  Result.TTV_Token_Bot := ReadSetting('edtBotToken');
  Result.TTV_Token_Strimer := ReadSetting('edtBotTokenStreamer');
  Result.TTV_Name_Bot := ReadSetting('edtBotName');
  Result.TTV_Name_Strimer := ReadSetting('edtChannel');
  Result.TTV_ClientID := ReadSetting('edtBotClientID');

  Result.DA_ClientID := ReadSetting('edtDAClientID');
  Result.DA_Client_Sictert := ReadSetting('edtDAClientSecret');
  Result.DA_RedirectURL := ReadSetting('edtDARedirectURL');
  Result.DA_Code := ReadSetting('edtDACode');

  Result.AI_Gigachat_ClientID := ReadSetting('edtGPTClientID');
  Result.AI_Gigachat_AutorizationCode := ReadSetting('edtGPTAC');
  Result.AI_ChatGPT_Token := ReadSetting('edtGPTATChatGPT');
  Result.AI_DeepSeek_Token := ReadSetting('edtGPTATDeepSeek');
end;

function TSettingsDatabase.GetValuesPlaceholders(Grid: TStringGrid): string;
var
  Col: Integer;
begin
  Result := '';
  for Col := 0 to Grid.ColumnCount - 1 do
  begin
    if Result <> '' then
      Result := Result + ', ';
    Result := Result + ':col' + IntToStr(Col);
  end;
end;

procedure TSettingsDatabase.addGroupResponse(Name, Respons: string);
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    Query.SQL.Text :=
      Format('INSERT INTO GroupResponse (Name, Response) VALUES (''%s'', ''%s'')',
      [Name, Respons]);
    Query.ExecSQL;
  finally
    Query.Free;
  end;
end;

function TSettingsDatabase.CheckTableExists(const TableName: string): Boolean;
var
  Query: TFDQuery;
begin
  Result := False;
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    Query.SQL.Text :=
      'SELECT COUNT(*) FROM sqlite_master WHERE type=''table'' AND name=:TableName';
    Query.ParamByName('TableName').AsString := TableName;
    Query.Open;
    Result := (Query.Fields[0].AsInteger > 0);
  finally
    Query.Free;
  end;
end;

procedure TSettingsDatabase.LoadGridFromTable(const TableName: string;
  Grid: TStringGrid);
var
  Query: TFDQuery;
  Col, Row: Integer;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;

    // Проверить наличие таблицы
    if not CheckTableExists(TableName) then
    begin
      // Таблица не существует, выходим из процедуры
      exit;
    end;

    Query.SQL.Text := 'SELECT * FROM ' + TableName;
    Query.Open;

    // Очищаем Grid
    Grid.RowCount := 0;

    // Заполняем Grid данными из таблицы
    while not Query.EOF do
    begin
      Row := Grid.RowCount;
      Grid.RowCount := Grid.RowCount + 1;

      for Col := 0 to Grid.ColumnCount - 1 do
      begin
        Grid.Cells[Col, Row] := Query.FieldByName('col' + IntToStr(Col)
          ).AsString;
      end;

      Query.Next;
    end;
  finally
    Query.Free;
  end;
end;

procedure TSettingsDatabase.SaveGridToTable(const TableName: string;
  Grid: TStringGrid);
var
  Query: TFDQuery;
  Col, Row: Integer;
begin
  // Удаляем старую таблицу, если она существует
  FConnection.ExecSQL('DROP TABLE IF EXISTS ' + TableName);

  // Создаем новую таблицу
  FConnection.ExecSQL('CREATE TABLE ' + TableName + ' (' +
    '  id INTEGER PRIMARY KEY AUTOINCREMENT,' + '  ' +
    GetColumnsDefinition(Grid) + ');');

  // Вставляем данные из Grid в таблицу
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    for Row := 0 to Grid.RowCount - 1 do
    begin
      Query.SQL.Text := 'INSERT INTO ' + TableName + ' (' + GetColumnsList(Grid)
        + ') VALUES (' + GetValuesPlaceholders(Grid) + ')';
      for Col := 0 to Grid.ColumnCount - 1 do
      begin
        Query.ParamByName('col' + IntToStr(Col)).AsString :=
          Grid.Cells[Col, Row];
      end;
      Query.ExecSQL;
    end;
  finally
    Query.Free;
  end;
end;

function TSettingsDatabase.GetSetting(const Name: string): string;
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := FConnection;
    Query.SQL.Text := 'SELECT value FROM params WHERE name = :name';
    Query.ParamByName('name').AsString := Name;
    Query.Open;
    if not Query.IsEmpty then
      Result := Query.FieldByName('value').AsString
    else
      Result := '';
  finally
    Query.Free;
  end;
end;

procedure TSettingsDatabase.SetSetting(const Name, Value: string);
begin
  FConnection.ExecSQL
    ('INSERT OR REPLACE INTO params (name, value) VALUES (:name, :value)',
    [Name, Value]);
end;

function TSettingsDatabase.ReadSetting(const Name: string;
  Default: string = ''): string;
begin
  Result := GetSetting(Name);
  if Result = '' then
    Result := Default;
end;

procedure TSettingsDatabase.WriteSetting(const Name, Value: string);
begin
  SetSetting(Name, Value);
end;

end.
