unit frm_ChatIA;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
  Vcl.Buttons, Vcl.Grids, Vcl.DBGrids, Vcl.OleCtrls, SHDocVw, Data.DB,
  FireDAC.Comp.Client, FireDAC.Stan.Param, System.JSON, System.IOUtils,
  uIAService, uAppTheme, System.Win.ComObj, Winapi.ActiveX;

type
  TfrmChatIA = class(TForm)
    splSplitter: TSplitter;
    pnlChatArea: TPanel;
    pnlInput: TPanel;
    btnSend: TBitBtn;
    memInput: TMemo;
    webChat: TWebBrowser;
    pnlResults: TPanel;
    pnlResultsHeader: TPanel;
    lblResultsTitle: TLabel;
    btnExcelExport: TSpeedButton;
    dbgResults: TDBGrid;
    pnlSQLInfo: TPanel;
    lblSQLLabel: TLabel;
    memSQL: TMemo;
    dsResults: TDataSource;
    qryResults: TFDQuery;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
    procedure memInputKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure dbgResultsDrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
    procedure dbgResultsTitleClick(Column: TColumn);
    procedure btnExcelExportClick(Sender: TObject);
  private
    FIAService: TIAService;
    FTempHtmlFile: string;
    FConversationHistory: TStringList;
    FLastSortColumn: string;
    FLastSortDirection: string;
    procedure AddMessageToHistory(const ARole, AMessage: string);
    procedure RefreshChatView;
    procedure ExportGridToExcel;
  public
    { Public declarations }
  end;

var
  frmChatIA: TfrmChatIA;

implementation

uses
  dmg_Main;

{$R *.dfm}

procedure TfrmChatIA.FormCreate(Sender: TObject);
begin
  FIAService := TIAService.Create;
  FConversationHistory := TStringList.Create;

  // Generar ruta única para archivo HTML temporal
  FTempHtmlFile := IncludeTrailingPathDelimiter(TPath.GetTempPath) + 'RA_ChatIA_' + FormatDateTime('hhnnsszzz', Now) + '.html';

  // Ocultar panel de resultados por defecto
  pnlResults.Width := 0;
  pnlResults.Visible := False;
  splSplitter.Visible := False;

  // Inicializar ordenación
  FLastSortColumn := '';
  FLastSortDirection := 'A';
end;

procedure TfrmChatIA.FormDestroy(Sender: TObject);
begin
  FIAService.Free;
  FConversationHistory.Free;

  // Intentar borrar archivo temporal de ayuda
  if FileExists(FTempHtmlFile) then
  begin
    try
      TFile.Delete(FTempHtmlFile);
    except
      // Ignorar errores si está bloqueado temporalmente
    end;
  end;
end;

procedure TfrmChatIA.FormShow(Sender: TObject);
begin
  TAppTheme.ApplyToForm(Self);
  pnlInput.Color := $00DDDDDD; // Gris claro para fondo del input

  // Aplicar colores de la cabecera
  pnlResultsHeader.Color := TAppTheme.COLOR_MAIN_RED;

  // Saludo inicial
  AddMessageToHistory('assistant', '¡Hola! Soy tu asistente de base de datos. Escribe cualquier consulta sobre facturación, clientes, albaranes o artículos y generaré el reporte para ti. ' +
    'Por ejemplo: <i>"facturacion del cliente EL CORTE INGLES durante febrero a julio agrupado por año y producto"</i>.');
end;

procedure TfrmChatIA.AddMessageToHistory(const ARole, AMessage: string);
var
  LTimeStr: string;
  LBubbleHTML: string;
  LClass: string;
begin
  LTimeStr := FormatDateTime('hh:nn', Now);
  LClass := ARole; // 'user' o 'assistant'

  LBubbleHTML :=
    '    <div class="message-wrapper ' + LClass + '-wrapper">' +
    '      <div class="message ' + LClass + '">' +
    '        <div class="messageText">' + AMessage + '</div>' +
    '        <div class="time">' + LTimeStr + '</div>' +
    '      </div>' +
    '    </div>';

  FConversationHistory.Add(LBubbleHTML);
  RefreshChatView;
end;

procedure TfrmChatIA.RefreshChatView;
var
  LHtml: string;
  LBody: string;
begin
  LBody := FConversationHistory.Text;

  LHtml :=
    '<!DOCTYPE html>' +
    '<html>' +
    '<head>' +
    '  <meta charset="utf-8">' +
    '  <style>' +
    '    body {' +
    '      font-family: "Segoe UI", Arial, sans-serif;' +
    '      background-color: #F4F6F9;' +
    '      margin: 0;' +
    '      padding: 15px;' +
    '      color: #333;' +
    '    }' +
    '    .chat-container {' +
    '      display: flex;' +
    '      flex-direction: column;' +
    '      gap: 12px;' +
    '    }' +
    '    .message-wrapper {' +
    '      display: flex;' +
    '      width: 100%;' +
    '    }' +
    '    .user-wrapper {' +
    '      justify-content: flex-end;' +
    '    }' +
    '    .assistant-wrapper {' +
    '      justify-content: flex-start;' +
    '    }' +
    '    .message {' +
    '      max-width: 80%;' +
    '      padding: 10px 14px;' +
    '      border-radius: 12px;' +
    '      line-height: 1.4;' +
    '      font-size: 13.5px;' +
    '      box-shadow: 0 1px 2px rgba(0,0,0,0.1);' +
    '    }' +
    '    .user {' +
    '      background: linear-gradient(135deg, #A12B2D, #7A1A1A);' +
    '      color: #FFFFFF;' +
    '      border-bottom-right-radius: 2px;' +
    '    }' +
    '    .assistant {' +
    '      background-color: #FFFFFF;' +
    '      color: #333333;' +
    '      border-bottom-left-radius: 2px;' +
    '      border-left: 4px solid #A12B2D;' +
    '    }' +
    '    .time {' +
    '      font-size: 10px;' +
    '      opacity: 0.6;' +
    '      text-align: right;' +
    '      margin-top: 4px;' +
    '    }' +
    '    .sql-hint {' +
    '      font-family: "Consolas", monospace;' +
    '      background-color: #F1F3F5;' +
    '      padding: 8px;' +
    '      border-radius: 4px;' +
    '      font-size: 12px;' +
    '      margin-top: 6px;' +
    '      border: 1px solid #E2E8F0;' +
    '      color: #C7254E;' +
    '    }' +
    '  </style>' +
    '  <script>' +
    '    function scrollToBottom() {' +
    '      window.scrollTo(0, document.body.scrollHeight);' +
    '    }' +
    '  </script>' +
    '</head>' +
    '<body onload="scrollToBottom();">' +
    '  <div class="chat-container">' +
         LBody +
    '  </div>' +
    '</body>' +
    '</html>';

  // Escribir archivo temporal y navegar
  try
    TFile.WriteAllText(FTempHtmlFile, LHtml, TEncoding.UTF8);
    webChat.Navigate('file:///' + FTempHtmlFile);
  except
    // Silenciar fallos de concurrencia al escribir archivo
  end;
end;

procedure TfrmChatIA.btnSendClick(Sender: TObject);
var
  LQuestion: string;
  LSQL: string;
  LExplanation: string;
  LErrorMsg: string;
begin
  LQuestion := Trim(memInput.Text);
  if LQuestion = '' then Exit;

  // Agregar pregunta del usuario
  AddMessageToHistory('user', LQuestion);
  memInput.Text := '';

  // Bloquear entrada mientras se procesa
  memInput.Enabled := False;
  btnSend.Enabled := False;
  Screen.Cursor := crHourGlass;
  try
    // Llamar al servicio de IA
    if FIAService.ProcessQuery(LQuestion, LSQL, LExplanation, LErrorMsg) then
    begin
      // Consulta exitosa
      AddMessageToHistory('assistant', 'He procesado tu consulta. ' + LExplanation);
      memSQL.Lines.Text := LSQL;

      // Ejecutar la consulta en la base de datos
      try
        qryResults.Close;
        qryResults.SQL.Text := LSQL;
        qryResults.Open;

        // Mostrar panel de resultados si hay registros
        pnlResults.Visible := True;
        splSplitter.Visible := True;
        pnlResults.Width := 450;
      except
        on E: Exception do
        begin
          AddMessageToHistory('assistant', 'Se generó la consulta SQL pero falló al ejecutarse en el servidor: <div class="sql-hint">' + E.Message + '</div>');
        end;
      end;
    end
    else
    begin
      // Fallo de procesamiento de IA
      AddMessageToHistory('assistant', 'Lo siento, no pude procesar tu solicitud: ' + LErrorMsg);
    end;
  finally
    memInput.Enabled := True;
    btnSend.Enabled := True;
    Screen.Cursor := crDefault;
    memInput.SetFocus;
    RefreshChatView;
  end;
end;

procedure TfrmChatIA.memInputKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (Key = VK_RETURN) and not (ssShift in Shift) then
  begin
    Key := 0; // Evitar sonido beep y salto de línea
    btnSend.Click;
  end;
end;

procedure TfrmChatIA.dbgResultsDrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  Grid: TDBGrid;
  IsOddRow: Boolean;
begin
  Grid := TDBGrid(Sender);

  // Aplicar colores alternos de fila de uAppTheme
  if not (gdSelected in State) then
  begin
    if Assigned(Grid.DataSource) and Assigned(Grid.DataSource.DataSet) and Grid.DataSource.DataSet.Active then
      IsOddRow := Odd(Grid.DataSource.DataSet.RecNo)
    else
      IsOddRow := False;

    if IsOddRow then
      Grid.Canvas.Brush.Color := TAppTheme.COLOR_GRID_ROW_ODD
    else
      Grid.Canvas.Brush.Color := TAppTheme.COLOR_GRID_ROW_EVEN;
  end;

  Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

procedure TfrmChatIA.dbgResultsTitleClick(Column: TColumn);
var
  LDataSet: TDataSet;
  SortStr: string;
begin
  if (dbgResults.DataSource = nil) or (dbgResults.DataSource.DataSet = nil) then Exit;
  LDataSet := dbgResults.DataSource.DataSet;
  if not LDataSet.Active then Exit;

  // Soporte de ordenación FireDAC en memoria
  if FLastSortColumn = Column.FieldName then
  begin
    if FLastSortDirection = 'A' then
      FLastSortDirection := 'D'
    else
      FLastSortDirection := 'A';
  end
  else
  begin
    FLastSortColumn := Column.FieldName;
    FLastSortDirection := 'A';
  end;

  if FLastSortDirection = 'D' then
    SortStr := Column.FieldName + ':D'
  else
    SortStr := Column.FieldName + ':A';

  try
    if LDataSet is TFDQuery then
      TFDQuery(LDataSet).IndexFieldNames := SortStr;
  except
    // Silenciar si algún campo no permite ordenación directa
  end;
end;

procedure TfrmChatIA.btnExcelExportClick(Sender: TObject);
begin
  if qryResults.Active and (qryResults.RecordCount > 0) then
    ExportGridToExcel
  else
    ShowMessage('No hay datos para exportar.');
end;

procedure TfrmChatIA.ExportGridToExcel;
var
  ExcelApp: OleVariant;
  Workbook: OleVariant;
  Sheet: OleVariant;
  SaveDialog: TSaveDialog;
  Col, Row: Integer;
  Bookmark: TBookmark;
begin
  SaveDialog := TSaveDialog.Create(nil);
  try
    SaveDialog.Filter := 'Archivos de Excel (*.xlsx)|*.xlsx';
    SaveDialog.Title := 'Exportar Resultados a Excel';
    SaveDialog.DefaultExt := 'xlsx';

    if SaveDialog.Execute then
    begin
      Screen.Cursor := crHourGlass;
      try
        ExcelApp := CreateOleObject('Excel.Application');
        ExcelApp.Visible := False;
        ExcelApp.DisplayAlerts := False;
        Workbook := ExcelApp.Workbooks.Add;
        Sheet := Workbook.ActiveSheet;

        // Escribir cabeceras
        for Col := 0 to dbgResults.Columns.Count - 1 do
        begin
          if dbgResults.Columns[Col].Visible then
          begin
            Sheet.Cells[1, Col + 1].Value := dbgResults.Columns[Col].Title.Caption;
            Sheet.Cells[1, Col + 1].Font.Bold := True;
          end;
        end;

        // Escribir registros
        Bookmark := qryResults.GetBookmark;
        qryResults.DisableControls;
        try
          qryResults.First;
          Row := 2;
          while not qryResults.Eof do
          begin
            for Col := 0 to dbgResults.Columns.Count - 1 do
            begin
              if dbgResults.Columns[Col].Visible then
                Sheet.Cells[Row, Col + 1].Value := dbgResults.Columns[Col].Field.Value;
            end;
            Inc(Row);
            qryResults.Next;
          end;
        finally
          qryResults.GotoBookmark(Bookmark);
          qryResults.FreeBookmark(Bookmark);
          qryResults.EnableControls;
        end;

        // Guardar libro y cerrar
        Workbook.SaveAs(SaveDialog.FileName);
        Workbook.Close(True);
        ExcelApp.Quit;
        ExcelApp := Unassigned;

        ShowMessage('Los resultados se han exportado correctamente a Excel.');
      except
        on E: Exception do
        begin
          Screen.Cursor := crDefault;
          ShowMessage('Error al exportar a Excel: ' + E.Message);
        end;
      end;
      Screen.Cursor := crDefault;
    end;
  finally
    SaveDialog.Free;
  end;
end;

end.
