unit dlg_SelectClientesEmail;

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, FireDAC.Comp.Client, Data.DB, System.UITypes, FireDAC.Stan.Param;

type
  TClienteEmailInfo = class
  public
    Id: string;
    NombreFiscal: string;
    NombreComercial: string;
    Nif: string;
    Email: string;
    Telefono: string;
    constructor Create(const AId, ANombreFiscal, ANombreComercial, ANif, AEmail, ATelefono: string);
  end;

  TdlgSelectClientesEmail = class(TForm)
    pnlMain: TPanel;
    pnlHeader: TPanel;
    lblTitulo: TLabel;
    lblGrupoInfo: TLabel;
    pnlConfig: TPanel;
    gbConfigEnvio: TGroupBox;
    lblCuenta: TLabel;
    lblAsunto: TLabel;
    lblMensaje: TLabel;
    lblCopia: TLabel;
    cboCuentasEnvio: TComboBox;
    edtEmailCopia: TEdit;
    edtAsunto: TEdit;
    edtMensajePreview: TEdit;
    pnlClientList: TPanel;
    gbClientes: TGroupBox;
    pnlListToolbar: TPanel;
    lblContador: TLabel;
    btnMarcarTodos: TBitBtn;
    btnDesmarcarTodos: TBitBtn;
    lvClientes: TListView;
    pnlBottom: TPanel;
    lblProgreso: TLabel;
    pbProgreso: TProgressBar;
    btnEnviar: TBitBtn;
    btnCancelar: TBitBtn;

    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure cboCuentasEnvioChange(Sender: TObject);
    procedure btnMarcarTodosClick(Sender: TObject);
    procedure btnDesmarcarTodosClick(Sender: TObject);
    procedure lvClientesItemChecked(Sender: TObject; Item: TListItem);
    procedure btnEnviarClick(Sender: TObject);
    procedure btnCancelarClick(Sender: TObject);
  private
    FGrupoId: string;
    FGrupoNombre: string;
    FRutaPDF: string;
    FReportName: string;
    FCuentasIDs: TStringList;

    procedure CargarCuentasEnvio;
    procedure CargarClientesGrupo;
    procedure ActualizarContador;
    function EsEmailValido(const AEmail: string): Boolean;
  public
    property GrupoId: string read FGrupoId write FGrupoId;
    property GrupoNombre: string read FGrupoNombre write FGrupoNombre;
    property RutaPDF: string read FRutaPDF write FRutaPDF;
    property ReportName: string read FReportName write FReportName;

    class function Execute(AOwner: TComponent; const AGrupoId, AGrupoNombre, ARutaPDF, AReportName: string): Boolean;
  end;

var
  dlgSelectClientesEmail: TdlgSelectClientesEmail;

implementation

uses
  dmg_Main, uAppTheme, uEmailWhatsApp;

{$R *.dfm}

{ TClienteEmailInfo }

constructor TClienteEmailInfo.Create(const AId, ANombreFiscal, ANombreComercial, ANif, AEmail, ATelefono: string);
begin
  Id := AId;
  NombreFiscal := ANombreFiscal;
  NombreComercial := ANombreComercial;
  Nif := ANif;
  Email := AEmail;
  Telefono := ATelefono;
end;

{ TdlgSelectClientesEmail }

class function TdlgSelectClientesEmail.Execute(AOwner: TComponent; const AGrupoId, AGrupoNombre, ARutaPDF, AReportName: string): Boolean;
var
  Dlg: TdlgSelectClientesEmail;
begin
  Dlg := TdlgSelectClientesEmail.Create(AOwner);
  try
    Dlg.GrupoId := AGrupoId;
    Dlg.GrupoNombre := AGrupoNombre;
    Dlg.RutaPDF := ARutaPDF;
    Dlg.ReportName := AReportName;
    Result := (Dlg.ShowModal = mrOk);
  finally
    Dlg.Free;
  end;
end;

procedure TdlgSelectClientesEmail.FormShow(Sender: TObject);
begin
  TAppTheme.ApplyToForm(Self);

  FCuentasIDs := TStringList.Create;

  lblGrupoInfo.Caption := Format('Grupo de Clientes: %s - Seleccione los destinatarios a los que enviar la tarifa:', [FGrupoNombre]);

  CargarCuentasEnvio;
  CargarClientesGrupo;
  ActualizarContador;
end;

procedure TdlgSelectClientesEmail.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  if Assigned(lvClientes) then
  begin
    for I := 0 to lvClientes.Items.Count - 1 do
    begin
      if lvClientes.Items[I].Data <> nil then
      begin
        TObject(lvClientes.Items[I].Data).Free;
        lvClientes.Items[I].Data := nil;
      end;
    end;
  end;

  if Assigned(FCuentasIDs) then
    FCuentasIDs.Free;
end;

procedure TdlgSelectClientesEmail.CargarCuentasEnvio;
var
  Qry: TFDQuery;
begin
  cboCuentasEnvio.Items.Clear;
  FCuentasIDs.Clear;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT id, nombre_perfil FROM ge_config_envio ORDER BY nombre_perfil';
    Qry.Open;

    while not Qry.Eof do
    begin
      cboCuentasEnvio.Items.Add(Qry.FieldByName('nombre_perfil').AsString);
      FCuentasIDs.Add(Qry.FieldByName('id').AsString);
      Qry.Next;
    end;
  finally
    Qry.Free;
  end;

  if cboCuentasEnvio.Items.Count > 0 then
  begin
    cboCuentasEnvio.ItemIndex := 0;
    cboCuentasEnvioChange(Self);
  end
  else
  begin
    edtAsunto.Text := 'Tarifa de Precios Oficial - ' + FGrupoNombre;
    edtMensajePreview.Text := 'Estimado cliente, adjuntamos la tarifa de precios oficial correspondiente a su cuenta.';
  end;
end;

procedure TdlgSelectClientesEmail.cboCuentasEnvioChange(Sender: TObject);
var
  LSelID: string;
  SmtpHost, SmtpUsuario, SmtpPassword, SmtpNombre: string;
  SmtpPuerto, WaModo: Integer;
  SmtpSSL: Boolean;
  WaTelefono, WaApiKey: string;
  TplEmailAsunto, TplEmailCuerpo, TplWaMensaje, FirmaHTML: string;
begin
  if (cboCuentasEnvio.ItemIndex < 0) or (cboCuentasEnvio.ItemIndex >= FCuentasIDs.Count) then Exit;

  LSelID := FCuentasIDs[cboCuentasEnvio.ItemIndex];
  LeerConfiguracion(LSelID, SmtpHost, SmtpPuerto, SmtpSSL, SmtpUsuario, SmtpPassword, SmtpNombre,
    WaModo, WaTelefono, WaApiKey, TplEmailAsunto, TplEmailCuerpo, TplWaMensaje, FirmaHTML);

  edtEmailCopia.Text := SmtpUsuario;

  if Trim(TplEmailAsunto) <> '' then
  begin
    edtAsunto.Text := StringReplace(TplEmailAsunto, '{documento}', 'Tarifa de Precios ' + FGrupoNombre, [rfReplaceAll, rfIgnoreCase]);
    edtAsunto.Text := StringReplace(edtAsunto.Text, '{empresa}', dmgMain.CurrentCompanyName, [rfReplaceAll, rfIgnoreCase]);
  end
  else
    edtAsunto.Text := 'Tarifa de Precios Oficial - ' + FGrupoNombre;

  if Trim(TplEmailCuerpo) <> '' then
  begin
    edtMensajePreview.Text := StringReplace(TplEmailCuerpo, '{documento}', 'la tarifa de precios oficial', [rfReplaceAll, rfIgnoreCase]);
    edtMensajePreview.Text := StringReplace(edtMensajePreview.Text, '{empresa}', dmgMain.CurrentCompanyName, [rfReplaceAll, rfIgnoreCase]);
  end
  else
    edtMensajePreview.Text := 'Estimado cliente, adjuntamos la tarifa de precios oficial correspondiente a su cuenta.';
end;

function TdlgSelectClientesEmail.EsEmailValido(const AEmail: string): Boolean;
var
  S: string;
begin
  S := Trim(AEmail);
  Result := (S <> '') and (Pos('@', S) > 1) and (Pos('.', S) > Pos('@', S) + 1);
end;

procedure TdlgSelectClientesEmail.CargarClientesGrupo;
var
  Qry: TFDQuery;
  LItem: TListItem;
  LInfo: TClienteEmailInfo;
  LEmail: string;
  LHasEmail: Boolean;
begin
  lvClientes.Items.BeginUpdate;
  try
    lvClientes.Items.Clear;

    Qry := TFDQuery.Create(nil);
    try
      Qry.Connection := dmgMain.dbConn;
      Qry.SQL.Text :=
        'SELECT id, nombre_fiscal, nombre_comercial, nif, email, telefono ' +
        'FROM ge_clientes ' +
        'WHERE clientesgrupo_id = :grp AND activo = 1 ' +
        'ORDER BY nombre_fiscal';
      Qry.ParamByName('grp').AsString := FGrupoId;
      Qry.Open;

      while not Qry.Eof do
      begin
        LEmail := Trim(Qry.FieldByName('email').AsString);
        LHasEmail := EsEmailValido(LEmail);

        LInfo := TClienteEmailInfo.Create(
          Qry.FieldByName('id').AsString,
          Qry.FieldByName('nombre_fiscal').AsString,
          Qry.FieldByName('nombre_comercial').AsString,
          Qry.FieldByName('nif').AsString,
          LEmail,
          Qry.FieldByName('telefono').AsString
        );

        LItem := lvClientes.Items.Add;
        LItem.Data := LInfo;
        LItem.Caption := LInfo.NombreFiscal;
        LItem.SubItems.Add(LInfo.NombreComercial);
        LItem.SubItems.Add(LInfo.Nif);
        LItem.SubItems.Add(LEmail);

        if LHasEmail then
        begin
          LItem.SubItems.Add('Listo');
          LItem.Checked := True; // Marcado por defecto si tiene email válido
        end
        else
        begin
          LItem.SubItems.Add('Sin email');
          LItem.Checked := False;
        end;

        Qry.Next;
      end;
    finally
      Qry.Free;
    end;
  finally
    lvClientes.Items.EndUpdate;
  end;
end;

procedure TdlgSelectClientesEmail.ActualizarContador;
var
  I, LSel, LTotal, LConEmail: Integer;
  LInfo: TClienteEmailInfo;
begin
  LTotal := lvClientes.Items.Count;
  LSel := 0;
  LConEmail := 0;

  for I := 0 to LTotal - 1 do
  begin
    if lvClientes.Items[I].Checked then
    begin
      Inc(LSel);
      LInfo := TClienteEmailInfo(lvClientes.Items[I].Data);
      if Assigned(LInfo) and EsEmailValido(LInfo.Email) then
        Inc(LConEmail);
    end;
  end;

  lblContador.Caption := Format('%d de %d clientes seleccionados (%d con correo válido)', [LSel, LTotal, LConEmail]);
  btnEnviar.Enabled := (LConEmail > 0);
end;

procedure TdlgSelectClientesEmail.btnMarcarTodosClick(Sender: TObject);
var
  I: Integer;
  LInfo: TClienteEmailInfo;
begin
  lvClientes.Items.BeginUpdate;
  try
    for I := 0 to lvClientes.Items.Count - 1 do
    begin
      LInfo := TClienteEmailInfo(lvClientes.Items[I].Data);
      if Assigned(LInfo) and EsEmailValido(LInfo.Email) then
        lvClientes.Items[I].Checked := True;
    end;
  finally
    lvClientes.Items.EndUpdate;
  end;
  ActualizarContador;
end;

procedure TdlgSelectClientesEmail.btnDesmarcarTodosClick(Sender: TObject);
var
  I: Integer;
begin
  lvClientes.Items.BeginUpdate;
  try
    for I := 0 to lvClientes.Items.Count - 1 do
      lvClientes.Items[I].Checked := False;
  finally
    lvClientes.Items.EndUpdate;
  end;
  ActualizarContador;
end;

procedure TdlgSelectClientesEmail.lvClientesItemChecked(Sender: TObject; Item: TListItem);
begin
  ActualizarContador;
end;

procedure TdlgSelectClientesEmail.btnCancelarClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

procedure TdlgSelectClientesEmail.btnEnviarClick(Sender: TObject);
var
  I, LTotalSeleccionados: Integer;
  LInfo: TClienteEmailInfo;
  LSelConfigID: string;
  LAsunto, LMensajeBase, LMensajeCliente: string;
  LEmailCopia: string;
  LSuccessCount, LErrorCount: Integer;
  LErrorMsg, LErroresDetalle: string;
begin
  if (cboCuentasEnvio.ItemIndex < 0) or (cboCuentasEnvio.ItemIndex >= FCuentasIDs.Count) then
  begin
    ShowMessage('Por favor, configure o seleccione una cuenta de envío SMTP remitente.');
    Exit;
  end;

  LSelConfigID := FCuentasIDs[cboCuentasEnvio.ItemIndex];

  if not FileExists(FRutaPDF) then
  begin
    ShowMessage('El archivo PDF de la tarifa adjunta no se ha encontrado:' + sLineBreak + FRutaPDF);
    Exit;
  end;

  // Contar seleccionados con email
  LTotalSeleccionados := 0;
  for I := 0 to lvClientes.Items.Count - 1 do
  begin
    if lvClientes.Items[I].Checked then
    begin
      LInfo := TClienteEmailInfo(lvClientes.Items[I].Data);
      if Assigned(LInfo) and EsEmailValido(LInfo.Email) then
        Inc(LTotalSeleccionados);
    end;
  end;

  if LTotalSeleccionados = 0 then
  begin
    ShowMessage('No hay ningún cliente seleccionado con dirección de correo electrónico válida.');
    Exit;
  end;

  if MessageDlg(
       Format('¿Está seguro de que desea enviar %d correos electrónicos individuales con la tarifa adjunta?' + sLineBreak + sLineBreak +
              'Cada cliente recibirá un correo independiente a su dirección.', [LTotalSeleccionados]),
       mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
    Exit;

  LAsunto := Trim(edtAsunto.Text);
  LMensajeBase := Trim(edtMensajePreview.Text);
  LEmailCopia := Trim(edtEmailCopia.Text);

  // Bloquear interfaz para evitar pulsaciones repetidas
  btnEnviar.Enabled := False;
  btnCancelar.Enabled := False;
  btnMarcarTodos.Enabled := False;
  btnDesmarcarTodos.Enabled := False;
  lvClientes.Enabled := False;
  cboCuentasEnvio.Enabled := False;

  lblProgreso.Visible := True;
  pbProgreso.Visible := True;
  pbProgreso.Min := 0;
  pbProgreso.Max := LTotalSeleccionados;
  pbProgreso.Position := 0;

  LSuccessCount := 0;
  LErrorCount := 0;
  LErroresDetalle := '';

  Screen.Cursor := crHourGlass;
  try
    for I := 0 to lvClientes.Items.Count - 1 do
    begin
      if lvClientes.Items[I].Checked then
      begin
        LInfo := TClienteEmailInfo(lvClientes.Items[I].Data);
        if Assigned(LInfo) and EsEmailValido(LInfo.Email) then
        begin
          lblProgreso.Caption := Format('Enviando (%d de %d): %s [%s]...',
            [LSuccessCount + LErrorCount + 1, LTotalSeleccionados, LInfo.NombreFiscal, LInfo.Email]);
          Application.ProcessMessages;

          // Personalizar mensaje si contiene la variable {nombre}
          LMensajeCliente := StringReplace(LMensajeBase, '{nombre}', LInfo.NombreFiscal, [rfReplaceAll, rfIgnoreCase]);
          if LMensajeCliente = LMensajeBase then
            LMensajeCliente := StringReplace(LMensajeBase, '{cliente}', LInfo.NombreFiscal, [rfReplaceAll, rfIgnoreCase]);

          if EnviarEmail(LSelConfigID, LInfo.Email, LAsunto, LMensajeCliente, [FRutaPDF], LEmailCopia, LErrorMsg) then
          begin
            Inc(LSuccessCount);
            lvClientes.Items[I].SubItems[3] := 'Enviado';
          end
          else
          begin
            Inc(LErrorCount);
            lvClientes.Items[I].SubItems[3] := 'Error envío';
            LErroresDetalle := LErroresDetalle + sLineBreak + '• ' + LInfo.NombreFiscal + ' (' + LInfo.Email + '): ' + LErrorMsg;
          end;

          pbProgreso.Position := LSuccessCount + LErrorCount;
          Application.ProcessMessages;
        end;
      end;
    end;
  finally
    Screen.Cursor := crDefault;
    btnCancelar.Enabled := True;
  end;

  lblProgreso.Caption := Format('Proceso finalizado: %d enviados correctamente, %d errores.', [LSuccessCount, LErrorCount]);

  if LErrorCount = 0 then
  begin
    ShowMessage(Format('¡Se han enviado con éxito los %d correos individuales a los clientes del grupo!', [LSuccessCount]));
    ModalResult := mrOk;
  end
  else
  begin
    ShowMessage(Format('Envío completado con incidencias:' + sLineBreak +
                       '• Enviados con éxito: %d' + sLineBreak +
                       '• Errores (%d):' + sLineBreak + '%s',
                       [LSuccessCount, LErrorCount, LErroresDetalle]));
    if LSuccessCount > 0 then
      ModalResult := mrOk;
  end;
end;

end.
