unit frm_ConfigEnvioEditor;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Buttons,
  Vcl.ComCtrls, Data.DB, FireDAC.Comp.Client, uAppTheme, FireDAC.Stan.Param, uBaseForm,
  Vcl.Grids, Vcl.DBGrids, IdCoderMIME, Winapi.ShellAPI, System.Math;

type
  TfrmConfigEnvioEditor = class(TfrmBase)
    lblEmpresa: TLabel;
    cbEmpresa: TComboBox;
    lblNombrePerfil: TLabel;
    edtNombrePerfil: TEdit;
    chkActivo: TCheckBox;
    
    // GroupBox SMTP
    grpSmtp: TGroupBox;
    lblProveedor: TLabel;
    cbProveedor: TComboBox;
    lblSmtpHost: TLabel;
    edtSmtpHost: TEdit;
    lblSmtpPuerto: TLabel;
    edtSmtpPuerto: TEdit;
    chkSmtpSSL: TCheckBox;
    lblSmtpUsuario: TLabel;
    edtSmtpUsuario: TEdit;
    lblSmtpPassword: TLabel;
    edtSmtpPassword: TEdit;
    lblSmtpNombre: TLabel;
    edtSmtpNombre: TEdit;
    btnProbarSmtp: TBitBtn;
    lblProbadoInfo: TLabel;

    // Pestaña Plantillas
    tsPlantillas: TTabSheet;
    pnlTplContent: TPanel;
    lblTplAsunto: TLabel;
    edtTplAsunto: TEdit;
    lblTplCuerpo: TLabel;
    memTplCuerpo: TMemo;
    lblTagsAyuda: TLabel;
    btnConvertirTplHtml: TBitBtn;

    // Pestaña Firma
    tsFirma: TTabSheet;
    pnlFirmaContent: TPanel;
    pnlFirmaTools: TPanel;
    btnInsertarImagen: TBitBtn;
    btnVistaPreviaFirma: TBitBtn;
    btnConvertirFirmaHtml: TBitBtn;
    grpFirmaHtml: TGroupBox;
    memFirmaHtml: TMemo;
    odImagenFirma: TOpenDialog;

    // Pestaña Canales Móviles
    tsCanalesMoviles: TTabSheet;
    pnlMovilContent: TPanel;
    grpWhatsApp: TGroupBox;
    lblWaModo: TLabel;
    cbWaModo: TComboBox;
    lblWaTelefono: TLabel;
    edtWaTelefono: TEdit;
    lblWaApiKey: TLabel;
    edtWaApiKey: TEdit;
    lblTplWa: TLabel;
    memTplWa: TMemo;
    grpTelegram: TGroupBox;
    lblTelegramInfo: TLabel;
    lblTgToken: TLabel;
    edtTgToken: TEdit;
    lblTgChatId: TLabel;
    edtTgChatId: TEdit;

    procedure FormShow(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnSalirClick(Sender: TObject);
    procedure cbProveedorChange(Sender: TObject);
    procedure btnProbarSmtpClick(Sender: TObject);
    procedure btnInsertarImagenClick(Sender: TObject);
    procedure btnVistaPreviaFirmaClick(Sender: TObject);
    procedure btnConvertirFirmaHtmlClick(Sender: TObject);
    procedure btnConvertirTplHtmlClick(Sender: TObject);
  protected
    procedure DoShow; override;
  private
    FConfigEnvioId: Integer;
    FParentForm: TForm;
    FEmpresaIds: TStringList;
    FProbadoState: Integer;

    procedure LoadCompanies;
    procedure LoadData;
    function ImagenABase64HTML(const ARutaImagen: string): string;
    function EsHtmlEstructurado(const AText: string): Boolean;
    function ConvertirTextoAHtmlFirma(const AText: string): string;
    function ConvertirTextoAHtmlCuerpo(const AText: string): string;
    function FormatearLineasConEnlaces(const ALine: string): string;
  public
    destructor Destroy; override;
    property ConfigEnvioId: Integer read FConfigEnvioId write FConfigEnvioId;
    property ParentForm: TForm read FParentForm write FParentForm;
  end;

var
  frmConfigEnvioEditor: TfrmConfigEnvioEditor;

implementation

uses
  dmg_Main, uDbErrorHandler, uEmailWhatsApp, System.UITypes;

{$R *.dfm}

destructor TfrmConfigEnvioEditor.Destroy;
begin
  if Assigned(FEmpresaIds) then
    FreeAndNil(FEmpresaIds);
  inherited Destroy;
end;

procedure TfrmConfigEnvioEditor.FormShow(Sender: TObject);
begin
  // Mantener evento para compatibilidad DFM
end;

procedure TfrmConfigEnvioEditor.DoShow;
begin
  inherited DoShow;

  TAppTheme.ApplyToForm(Self);

  btnGuardar.OnClick := btnSaveClick;
  btnSalir.OnClick := btnSalirClick;

  pgcDetails.Visible := True;
  TsListado.TabVisible := False;
  tsDatosEnvio.Caption := '1. Servidor y Cuenta SMTP';
  tsPlantillas.Caption := '2. Plantillas de Mensaje';
  tsFirma.Caption := '3. Firma de Correo (HTML)';
  tsCanalesMoviles.Caption := '4. WhatsApp y Telegram';
  pgcDetails.ActivePage := tsDatosEnvio;

  if not Assigned(FEmpresaIds) then
    FEmpresaIds := TStringList.Create
  else
    FEmpresaIds.Clear;

  // Llenar combo de proveedores
  cbProveedor.Items.Clear;
  cbProveedor.Items.Add('Personalizado / Servidor Propio (SMTP)');
  cbProveedor.Items.Add('Gmail (Google)');
  cbProveedor.Items.Add('Outlook / Office 365 / Hotmail (Microsoft)');
  cbProveedor.Items.Add('Yahoo Mail');
  cbProveedor.ItemIndex := 0;

  LoadCompanies;

  if FConfigEnvioId > 0 then
    LoadData
  else
  begin
    edtNombrePerfil.Text := 'Perfil ' + dmgMain.CurrentCompanyName;
    edtSmtpHost := edtSmtpHost;
    edtSmtpPuerto.Text := '587';
    chkSmtpSSL.Checked := True;
    edtSmtpUsuario.Text := '';
    edtSmtpPassword.Text := '';
    edtSmtpNombre.Text := dmgMain.CurrentCompanyName;
    FProbadoState := 0;
    lblProbadoInfo.Caption := 'Estado: Sin verificar';
    lblProbadoInfo.Font.Color := clGray;

    edtTplAsunto.Text := 'Envío de documento de {empresa} - Ref: {documento}';
    memTplCuerpo.Lines.Text :=
      'Estimado/a cliente,' + sLineBreak + sLineBreak +
      'Adjuntamos a este correo el documento correspondiente a su solicitud con fecha {fecha}.' + sLineBreak + sLineBreak +
      'Para cualquier consulta o aclaración, no dude en ponerse en contacto con nosotros.' + sLineBreak + sLineBreak +
      'Atentamente,' + sLineBreak +
      '{empresa}';

    memFirmaHtml.Lines.Text :=
      '<div style="font-family: Segoe UI, Arial, sans-serif; font-size: 13px; color: #333333; line-height: 1.5;">' + sLineBreak +
      '  <p style="margin: 0; font-weight: bold; color: #1e3a8a;">' + dmgMain.CurrentCompanyName + '</p>' + sLineBreak +
      '  <p style="margin: 0; color: #64748b;">Departamento de Administración y Facturación</p>' + sLineBreak +
      '  <p style="margin: 4px 0; font-size: 11px; color: #94a3b8;">' +
      'Este mensaje y sus archivos adjuntos son confidenciales. Si lo ha recibido por error, por favor notifíquelo al remitente.</p>' + sLineBreak +
      '</div>';

    cbWaModo.ItemIndex := 0;
    edtWaTelefono.Text := '';
    edtWaApiKey.Text := '';
    memTplWa.Lines.Text := 'Hola {nombre}, le enviamos su documento de {empresa}. Muchas gracias por confiar en nosotros.';
    edtTgToken.Text := '';
    edtTgChatId.Text := '';
    chkActivo.Checked := True;
  end;

  ResetChangeTracking;
  ResetModifiedState;
end;

procedure TfrmConfigEnvioEditor.LoadCompanies;
var
  LQry: TFDQuery;
  LIdx: Integer;
begin
  cbEmpresa.Items.Clear;
  FEmpresaIds.Clear;

  LQry := TFDQuery.Create(nil);
  try
    LQry.Connection := dmgMain.dbConn;
    LQry.SQL.Text := 'SELECT id, nombre FROM ge_empresas WHERE activo = 1 ORDER BY nombre';
    LQry.Open;
    while not LQry.Eof do
    begin
      cbEmpresa.Items.Add(LQry.FieldByName('nombre').AsString);
      FEmpresaIds.Add(LQry.FieldByName('id').AsString);
      LQry.Next;
    end;
  finally
    LQry.Free;
  end;

  if cbEmpresa.Items.Count > 0 then
  begin
    LIdx := FEmpresaIds.IndexOf(dmgMain.CurrentCompanyId);
    if LIdx >= 0 then
      cbEmpresa.ItemIndex := LIdx
    else
      cbEmpresa.ItemIndex := 0;
  end;
end;

procedure TfrmConfigEnvioEditor.LoadData;
var
  LQry: TFDQuery;
  LEmpresaId: string;
  LIdx: Integer;
begin
  LQry := TFDQuery.Create(nil);
  try
    LQry.Connection := dmgMain.dbConn;
    LQry.SQL.Text := 'SELECT * FROM ge_config_envio WHERE id = :id';
    LQry.ParamByName('id').AsInteger := FConfigEnvioId;
    LQry.Open;

    if not LQry.IsEmpty then
    begin
      LEmpresaId := LQry.FieldByName('empresa_id').AsString;
      LIdx := FEmpresaIds.IndexOf(LEmpresaId);
      if LIdx >= 0 then
        cbEmpresa.ItemIndex := LIdx;

      edtNombrePerfil.Text := LQry.FieldByName('nombre_perfil').AsString;
      edtSmtpHost.Text := LQry.FieldByName('smtp_host').AsString;
      edtSmtpPuerto.Text := LQry.FieldByName('smtp_puerto').AsString;
      chkSmtpSSL.Checked := (LQry.FieldByName('smtp_ssl').AsInteger = 1);
      edtSmtpUsuario.Text := LQry.FieldByName('smtp_usuario').AsString;
      edtSmtpPassword.Text := LQry.FieldByName('smtp_password').AsString;
      edtSmtpNombre.Text := LQry.FieldByName('smtp_nombre').AsString;
      
      FProbadoState := LQry.FieldByName('smtp_probado').AsInteger;
      if FProbadoState = 1 then
      begin
        lblProbadoInfo.Caption := '✔ Conexión SMTP verificada y probada';
        lblProbadoInfo.Font.Color := clGreen;
      end
      else
      begin
        lblProbadoInfo.Caption := '⚠ Estado: Sin verificar o pendiente de prueba';
        lblProbadoInfo.Font.Color := clMaroon;
      end;

      edtTplAsunto.Text := LQry.FieldByName('tpl_email_asunto').AsString;
      memTplCuerpo.Lines.Text := LQry.FieldByName('tpl_email_cuerpo').AsString;
      memFirmaHtml.Lines.Text := LQry.FieldByName('firma_html').AsString;

      cbWaModo.ItemIndex := LQry.FieldByName('wa_modo').AsInteger;
      if cbWaModo.ItemIndex < 0 then cbWaModo.ItemIndex := 0;
      edtWaTelefono.Text := LQry.FieldByName('wa_telefono').AsString;
      edtWaApiKey.Text := LQry.FieldByName('wa_apikey').AsString;
      memTplWa.Lines.Text := LQry.FieldByName('tpl_wa_mensaje').AsString;

      chkActivo.Checked := (LQry.FieldByName('activo').AsInteger = 1);
    end;
  finally
    LQry.Free;
  end;
end;

procedure TfrmConfigEnvioEditor.cbProveedorChange(Sender: TObject);
begin
  case cbProveedor.ItemIndex of
    1: // Gmail
      begin
        edtSmtpHost.Text := 'smtp.gmail.com';
        edtSmtpPuerto.Text := '587';
        chkSmtpSSL.Checked := True;
        ShowMessage('Nota para Gmail: Si la cuenta tiene verificación en 2 pasos activada, ' +
                    'debe generar y usar una "Contraseña de Aplicación" (App Password) de Google ' +
                    'en lugar de la contraseña habitual (Puerto: 587 TLS).');
      end;
    2: // Outlook / Office 365
      begin
        edtSmtpHost.Text := 'smtp.office365.com';
        edtSmtpPuerto.Text := '587';
        chkSmtpSSL.Checked := True;
      end;
    3: // Yahoo Mail
      begin
        edtSmtpHost.Text := 'smtp.mail.yahoo.com';
        edtSmtpPuerto.Text := '587';
        chkSmtpSSL.Checked := True;
      end;
  end;
end;

procedure TfrmConfigEnvioEditor.btnProbarSmtpClick(Sender: TObject);
var
  LErrorMsg, LPassword: string;
  LPuerto: Integer;
begin
  if Trim(edtSmtpHost.Text) = '' then
  begin
    ShowMessage('Por favor, introduzca el servidor SMTP.');
    edtSmtpHost.SetFocus;
    Exit;
  end;

  LPuerto := StrToIntDef(Trim(edtSmtpPuerto.Text), 587);
  LPassword := Trim(edtSmtpPassword.Text);
  // Si la contraseña de aplicación fue pegada con espacios (formato típico de Google 4x4)
  if (Pos('gmail', LowerCase(edtSmtpHost.Text)) > 0) and (Pos(' ', LPassword) > 0) then
    LPassword := StringReplace(LPassword, ' ', '', [rfReplaceAll]);

  Screen.Cursor := crHourGlass;
  try
    if ProbarConexionSMTP(edtSmtpHost.Text, LPuerto, chkSmtpSSL.Checked,
                          Trim(edtSmtpUsuario.Text), LPassword,
                          Trim(edtSmtpNombre.Text), LErrorMsg) then
    begin
      FProbadoState := 1;
      lblProbadoInfo.Caption := '✔ Conexión SMTP verificada con éxito (' + FormatDateTime('hh:nn:ss', Now) + ')';
      lblProbadoInfo.Font.Color := clGreen;
      ShowMessage('¡Conexión y autenticación SMTP exitosa!' + sLineBreak +
                  'El servidor ha respondido y autenticado la cuenta correctamente.');
    end
    else
    begin
      FProbadoState := 0;
      lblProbadoInfo.Caption := '❌ Error de conexión SMTP';
      lblProbadoInfo.Font.Color := clRed;
      ShowMessage('Error al probar la conexión SMTP:' + sLineBreak + sLineBreak + LErrorMsg);
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

function TfrmConfigEnvioEditor.ImagenABase64HTML(const ARutaImagen: string): string;
var
  FS: TFileStream;
  B64: string;
  Ext, MimeType: string;
begin
  Result := '';
  if not FileExists(ARutaImagen) then Exit;

  Ext := LowerCase(ExtractFileExt(ARutaImagen));
  if (Ext = '.jpg') or (Ext = '.jpeg') then MimeType := 'image/jpeg'
  else if Ext = '.png' then MimeType := 'image/png'
  else if Ext = '.gif' then MimeType := 'image/gif'
  else if Ext = '.bmp' then MimeType := 'image/bmp'
  else if Ext = '.svg' then MimeType := 'image/svg+xml'
  else MimeType := 'image/jpeg';

  FS := TFileStream.Create(ARutaImagen, fmOpenRead or fmShareDenyWrite);
  try
    B64 := TIdEncoderMIME.EncodeStream(FS);
  finally
    FS.Free;
  end;

  Result := '<img src="data:' + MimeType + ';base64,' + B64 + '" style="max-width:250px; height:auto; margin: 4px 0;" alt="Logo" />';
end;

procedure TfrmConfigEnvioEditor.btnInsertarImagenClick(Sender: TObject);
var
  LImgTag: string;
begin
  if odImagenFirma.Execute then
  begin
    LImgTag := ImagenABase64HTML(odImagenFirma.FileName);
    if LImgTag <> '' then
    begin
      memFirmaHtml.SelText := LImgTag;
      ShowMessage('Imagen incrustada en formato Base64 correctamente en la firma HTML.');
    end;
  end;
end;

function TfrmConfigEnvioEditor.EsHtmlEstructurado(const AText: string): Boolean;
var
  L: string;
begin
  L := LowerCase(Trim(AText));
  if L = '' then Exit(True);

  Result := ((Pos('<div', L) = 1) or (Pos('<!doctype', L) = 1) or (Pos('<html', L) = 1) or (Pos('<table', L) = 1) or (Pos('<p', L) = 1)) and
            ((Pos('</div>', L) > 0) or (Pos('</html>', L) > 0) or (Pos('</table>', L) > 0) or (Pos('</p>', L) > 0));
end;

function TfrmConfigEnvioEditor.FormatearLineasConEnlaces(const ALine: string): string;
var
  Words: TArray<string>;
  W, WClean, WLower, TrailingPunct: string;
  I: Integer;
  LRes: TStringBuilder;
begin
  LRes := TStringBuilder.Create;
  try
    Words := ALine.Split([' ']);
    for I := 0 to High(Words) do
    begin
      W := Words[I];
      TrailingPunct := '';

      while (Length(W) > 0) and CharInSet(W[Length(W)], ['.', ',', ';', ':', ')', ']', '>', '"', '''']) do
      begin
        TrailingPunct := W[Length(W)] + TrailingPunct;
        SetLength(W, Length(W) - 1);
      end;

      WClean := W;
      WLower := LowerCase(WClean);

      if (Pos('http://', WLower) = 1) or (Pos('https://', WLower) = 1) then
      begin
        LRes.Append('<a href="' + WClean + '" target="_blank" style="color: #2563eb; text-decoration: none;">' + WClean + '</a>' + TrailingPunct);
      end
      else if (Pos('www.', WLower) = 1) then
      begin
        LRes.Append('<a href="http://' + WClean + '" target="_blank" style="color: #2563eb; text-decoration: none;">' + WClean + '</a>' + TrailingPunct);
      end
      else if (Pos('@', WClean) > 1) and (Pos('.', WClean) > Pos('@', WClean) + 1) then
      begin
        LRes.Append('<a href="mailto:' + WClean + '" style="color: #2563eb; text-decoration: none;">' + WClean + '</a>' + TrailingPunct);
      end
      else
      begin
        LRes.Append(WClean + TrailingPunct);
      end;

      if I < High(Words) then
        LRes.Append(' ');
    end;
    Result := LRes.ToString;
  finally
    LRes.Free;
  end;
end;

function TfrmConfigEnvioEditor.ConvertirTextoAHtmlFirma(const AText: string): string;
var
  Lines: TStringList;
  I: Integer;
  Line, LLower: string;
  SB: TStringBuilder;
  IsFirst: Boolean;
begin
  if Trim(AText) = '' then Exit('');

  Lines := TStringList.Create;
  SB := TStringBuilder.Create;
  try
    Lines.Text := AText;
    SB.AppendLine('<div style="font-family: ''Segoe UI'', Arial, Helvetica, sans-serif; font-size: 13px; color: #333333; line-height: 1.5;">');

    IsFirst := True;
    for I := 0 to Lines.Count - 1 do
    begin
      Line := Trim(Lines[I]);
      if Line = '' then Continue;

      LLower := LowerCase(Line);

      if (Pos('<img', LLower) > 0) or (Pos('<table', LLower) > 0) or (Pos('<hr', LLower) > 0) then
      begin
        SB.AppendLine('  <div style="margin: 6px 0;">' + Line + '</div>');
        Continue;
      end;

      if (Pos('<p', LLower) = 1) or (Pos('<div', LLower) = 1) then
      begin
        SB.AppendLine('  ' + Line);
        IsFirst := False;
        Continue;
      end;

      Line := FormatearLineasConEnlaces(Line);

      if (Pos('confidencial', LLower) > 0) or (Pos('aviso legal', LLower) > 0) or 
         (Pos('rgpd', LLower) > 0) or (Pos('lopd', LLower) > 0) or 
         (Pos('este mensaje', LLower) > 0) or (Pos('destinatario', LLower) > 0) then
      begin
        SB.AppendLine('  <p style="margin: 8px 0 0 0; font-size: 11px; color: #94a3b8; line-height: 1.35; border-top: 1px solid #e2e8f0; padding-top: 6px;">' + Line + '</p>');
      end
      else if IsFirst then
      begin
        SB.AppendLine('  <p style="margin: 0 0 3px 0; font-weight: bold; color: #1e3a8a; font-size: 14px;">' + Line + '</p>');
        IsFirst := False;
      end
      else
      begin
        SB.AppendLine('  <p style="margin: 0 0 2px 0; font-size: 12.5px; color: #334155;">' + Line + '</p>');
      end;
    end;

    SB.Append('</div>');
    Result := SB.ToString;
  finally
    Lines.Free;
    SB.Free;
  end;
end;

function TfrmConfigEnvioEditor.ConvertirTextoAHtmlCuerpo(const AText: string): string;
var
  Lines: TStringList;
  I: Integer;
  Line: string;
  SB: TStringBuilder;
begin
  if Trim(AText) = '' then Exit('');

  Lines := TStringList.Create;
  SB := TStringBuilder.Create;
  try
    Lines.Text := AText;
    SB.AppendLine('<div style="font-family: ''Segoe UI'', Arial, Helvetica, sans-serif; font-size: 13.5px; color: #222222; line-height: 1.6;">');

    for I := 0 to Lines.Count - 1 do
    begin
      Line := Trim(Lines[I]);
      if Line = '' then
        SB.AppendLine('  <p style="margin: 0 0 10px 0;">&nbsp;</p>')
      else if (Pos('<p', LowerCase(Line)) > 0) or (Pos('<div', LowerCase(Line)) > 0) or (Pos('<br', LowerCase(Line)) > 0) then
        SB.AppendLine('  ' + Line)
      else
      begin
        Line := FormatearLineasConEnlaces(Line);
        SB.AppendLine('  <p style="margin: 0 0 10px 0;">' + Line + '</p>');
      end;
    end;

    SB.Append('</div>');
    Result := SB.ToString;
  finally
    Lines.Free;
    SB.Free;
  end;
end;

procedure TfrmConfigEnvioEditor.btnConvertirFirmaHtmlClick(Sender: TObject);
begin
  if Trim(memFirmaHtml.Lines.Text) = '' then
  begin
    ShowMessage('No hay texto en la firma para convertir.');
    Exit;
  end;

  if EsHtmlEstructurado(memFirmaHtml.Lines.Text) then
  begin
    if MessageDlg('La firma ya contiene estructura HTML.' + sLineBreak +
                  '¿Desea reestructurar y formatear el contenido a la plantilla estándar HTML?',
                  mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
      Exit;
  end;

  memFirmaHtml.Lines.Text := ConvertirTextoAHtmlFirma(memFirmaHtml.Lines.Text);
  ShowMessage('Firma convertida a formato HTML correctamente.');
end;

procedure TfrmConfigEnvioEditor.btnConvertirTplHtmlClick(Sender: TObject);
begin
  if Trim(memTplCuerpo.Lines.Text) = '' then
  begin
    ShowMessage('El cuerpo de la plantilla está vacío.');
    Exit;
  end;

  if EsHtmlEstructurado(memTplCuerpo.Lines.Text) then
  begin
    if MessageDlg('El cuerpo ya contiene estructura HTML.' + sLineBreak +
                  '¿Desea reestructurar y formatear el contenido a HTML estándar?',
                  mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
      Exit;
  end;

  memTplCuerpo.Lines.Text := ConvertirTextoAHtmlCuerpo(memTplCuerpo.Lines.Text);
  ShowMessage('Cuerpo de plantilla convertido a formato HTML correctamente.');
end;

procedure TfrmConfigEnvioEditor.btnVistaPreviaFirmaClick(Sender: TObject);
var
  TempPath, HtmlPath: string;
  FS: TFileStream;
  HTMLDoc: AnsiString;
  LHtmlFirma: string;
begin
  if Trim(memFirmaHtml.Lines.Text) = '' then
  begin
    ShowMessage('La firma HTML está vacía.');
    Exit;
  end;

  LHtmlFirma := memFirmaHtml.Lines.Text;
  if not EsHtmlEstructurado(LHtmlFirma) then
    LHtmlFirma := ConvertirTextoAHtmlFirma(LHtmlFirma);

  TempPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)) + 'tmp');
  ForceDirectories(TempPath);
  HtmlPath := TempPath + 'firma_preview_gestion.html';

  HTMLDoc := AnsiString(
    '<!DOCTYPE html><html><head><meta charset="UTF-8">' +
    '<title>Vista Previa de Firma</title>' +
    '<style>body { font-family: Segoe UI, Arial, sans-serif; padding: 20px; background-color: #f8fafc; }</style>' +
    '</head><body>' +
    '<div style="background: white; padding: 24px; border-radius: 8px; border: 1px solid #e2e8f0; max-width: 650px;">' +
    '<h4 style="color: #475569; margin-top: 0;">Vista Previa de la Firma de Correo</h4>' +
    '<hr style="border: none; border-top: 1px solid #cbd5e1; margin: 15px 0;">' +
    LHtmlFirma +
    '</div></body></html>');

  FS := TFileStream.Create(HtmlPath, fmCreate);
  try
    FS.WriteBuffer(HTMLDoc[1], Length(HTMLDoc));
  finally
    FS.Free;
  end;

  ShellExecute(0, 'open', PChar(HtmlPath), nil, nil, SW_SHOWNORMAL);
end;

procedure TfrmConfigEnvioEditor.btnSaveClick(Sender: TObject);
var
  LEmpresaId: Integer;
  LPassword: string;
begin
  if Trim(edtNombrePerfil.Text) = '' then
  begin
    ShowMessage('El nombre del perfil es obligatorio.');
    edtNombrePerfil.SetFocus;
    Exit;
  end;

  if (cbEmpresa.ItemIndex < 0) or (FEmpresaIds.Count = 0) then
  begin
    ShowMessage('Debe seleccionar una empresa.');
    cbEmpresa.SetFocus;
    Exit;
  end;

  LEmpresaId := StrToIntDef(FEmpresaIds[cbEmpresa.ItemIndex], 0);
  if LEmpresaId = 0 then
  begin
    ShowMessage('Identificador de empresa inválido.');
    Exit;
  end;

  try
    dmgMain.qryExec.Close;
    if FConfigEnvioId > 0 then
    begin
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_config_envio SET ' +
        '  empresa_id = :empresa_id, nombre_perfil = :nombre_perfil, smtp_host = :smtp_host, ' +
        '  smtp_puerto = :smtp_puerto, smtp_ssl = :smtp_ssl, smtp_usuario = :smtp_usuario, ' +
        '  smtp_password = :smtp_password, smtp_nombre = :smtp_nombre, smtp_probado = :smtp_probado, ' +
        '  wa_modo = :wa_modo, wa_telefono = :wa_telefono, wa_apikey = :wa_apikey, ' +
        '  tpl_email_asunto = :tpl_email_asunto, tpl_email_cuerpo = :tpl_email_cuerpo, ' +
        '  tpl_wa_mensaje = :tpl_wa_mensaje, firma_html = :firma_html, activo = :activo, ' +
        '  id_user_update = :user, updated_at = CURRENT_TIMESTAMP ' +
        'WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsInteger := FConfigEnvioId;
    end
    else
    begin
      dmgMain.qryExec.SQL.Text :=
        'INSERT INTO ge_config_envio (empresa_id, nombre_perfil, smtp_host, smtp_puerto, smtp_ssl, ' +
        '  smtp_usuario, smtp_password, smtp_nombre, smtp_probado, wa_modo, wa_telefono, wa_apikey, ' +
        '  tpl_email_asunto, tpl_email_cuerpo, tpl_wa_mensaje, firma_html, activo, ' +
        '  id_user_creator, id_user_update) ' +
        'VALUES (:empresa_id, :nombre_perfil, :smtp_host, :smtp_puerto, :smtp_ssl, ' +
        '  :smtp_usuario, :smtp_password, :smtp_nombre, :smtp_probado, :wa_modo, :wa_telefono, :wa_apikey, ' +
        '  :tpl_email_asunto, :tpl_email_cuerpo, :tpl_wa_mensaje, :firma_html, :activo, ' +
        '  :user, :user)';
    end;

    dmgMain.qryExec.ParamByName('empresa_id').AsInteger := LEmpresaId;
    dmgMain.qryExec.ParamByName('nombre_perfil').AsString := Trim(edtNombrePerfil.Text);
    dmgMain.qryExec.ParamByName('smtp_host').AsString := Trim(edtSmtpHost.Text);
    dmgMain.qryExec.ParamByName('smtp_puerto').AsInteger := StrToIntDef(Trim(edtSmtpPuerto.Text), 587);

    if chkSmtpSSL.Checked then
      dmgMain.qryExec.ParamByName('smtp_ssl').AsInteger := 1
    else
      dmgMain.qryExec.ParamByName('smtp_ssl').AsInteger := 0;

    dmgMain.qryExec.ParamByName('smtp_usuario').AsString := Trim(edtSmtpUsuario.Text);
    
    LPassword := Trim(edtSmtpPassword.Text);
    if (Pos('gmail', LowerCase(edtSmtpHost.Text)) > 0) and (Pos(' ', LPassword) > 0) then
      LPassword := StringReplace(LPassword, ' ', '', [rfReplaceAll]);
    dmgMain.qryExec.ParamByName('smtp_password').AsString := LPassword;

    dmgMain.qryExec.ParamByName('smtp_nombre').AsString := Trim(edtSmtpNombre.Text);
    dmgMain.qryExec.ParamByName('smtp_probado').AsInteger := FProbadoState;

    dmgMain.qryExec.ParamByName('wa_modo').AsInteger := cbWaModo.ItemIndex;
    dmgMain.qryExec.ParamByName('wa_telefono').AsString := Trim(edtWaTelefono.Text);
    dmgMain.qryExec.ParamByName('wa_apikey').AsString := Trim(edtWaApiKey.Text);

    dmgMain.qryExec.ParamByName('tpl_email_asunto').AsString := Trim(edtTplAsunto.Text);
    dmgMain.qryExec.ParamByName('tpl_email_cuerpo').DataType := ftWideMemo;
    dmgMain.qryExec.ParamByName('tpl_email_cuerpo').Size := Max(100000, Length(memTplCuerpo.Lines.Text) * 2 + 1024);
    dmgMain.qryExec.ParamByName('tpl_email_cuerpo').AsWideMemo := memTplCuerpo.Lines.Text;

    dmgMain.qryExec.ParamByName('tpl_wa_mensaje').AsString := memTplWa.Lines.Text;

    // Convertir automáticamente la firma a formato HTML si contiene texto plano sin etiquetas estructurales
    if (Trim(memFirmaHtml.Lines.Text) <> '') and not EsHtmlEstructurado(memFirmaHtml.Lines.Text) then
      memFirmaHtml.Lines.Text := ConvertirTextoAHtmlFirma(memFirmaHtml.Lines.Text);

    dmgMain.qryExec.ParamByName('firma_html').DataType := ftWideMemo;
    dmgMain.qryExec.ParamByName('firma_html').Size := Max(5000000, Length(memFirmaHtml.Lines.Text) * 2 + 1024);
    dmgMain.qryExec.ParamByName('firma_html').AsWideMemo := memFirmaHtml.Lines.Text;

    if chkActivo.Checked then
      dmgMain.qryExec.ParamByName('activo').AsInteger := 1
    else
      dmgMain.qryExec.ParamByName('activo').AsInteger := 0;

    dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
    dmgMain.qryExec.ExecSQL;

    ResetChangeTracking;
    ResetModifiedState;
    ModalResult := mrOk;
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, 'Error al guardar la configuración de envío');
  end;
end;

procedure TfrmConfigEnvioEditor.btnSalirClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

end.
