unit uSifCrypto;

interface

uses
  System.SysUtils, System.Hash, uVeriFactuTypes;

type
  /// <summary>
  /// Funciones criptograficas y generacion de identificadores para VeriFactu / SIF
  /// segun especificaciones de la Orden HAC/1177/2024.
  /// </summary>
  TSifCrypto = class
  public
    /// <summary>
    /// Calcula la huella digital SHA-256 en mayusculas hexadecimales a partir de la
    /// cadena normalizada de campos segun la normativa SIF.
    /// </summary>
    class function CalcularHuellaSHA256(const AData: THashInputData): string; static;

    /// <summary>
    /// Genera la URL oficial de cotejo para el codigo QR segun el modo de operacion
    /// (Preproduccion / Produccion) con los parametros nif, numserie, fecha, importe y huella.
    /// </summary>
    class function GenerarUrlQR(AModo: TSifModoOperacion;
      const ANifEmisor, ASerieNumero, AFechaExpedicion: string;
      AImporteTotal: Double; const AHuellaCompleta: string): string; static;

    /// <summary>
    /// Extrae los primeros 8 caracteres de la huella hash para el parametro QR.
    /// </summary>
    class function ExtraerPrefijoHuella(const AHuellaCompleta: string): string; static;
  end;

implementation

class function TSifCrypto.CalcularHuellaSHA256(const AData: THashInputData): string;
var
  LCadenaNormalizada: string;
  LFormatSettings: TFormatSettings;
begin
  LFormatSettings := TFormatSettings.Create('es-ES');
  LFormatSettings.DecimalSeparator := '.';

  // Concatenacion oficial con separador & :
  // IDEmisorFactura=...&NumSerieFactura=...&FechaExpedicionFactura=...&TipoFactura=...&CuotaTotal=...&ImporteTotal=...&Huella=...&FechaHoraHusoGenRegistro=...
  LCadenaNormalizada :=
    'IDEmisorFactura=' + Trim(AData.IDEmisorFactura) +
    '&NumSerieFactura=' + Trim(AData.NumSerieFactura) +
    '&FechaExpedicionFactura=' + Trim(AData.FechaExpedicionFactura) +
    '&TipoFactura=' + Trim(AData.TipoFactura) +
    '&CuotaTotal=' + FloatToStrF(AData.CuotaTotal, ffFixed, 12, 2, LFormatSettings) +
    '&ImporteTotal=' + FloatToStrF(AData.ImporteTotal, ffFixed, 12, 2, LFormatSettings) +
    '&Huella=' + Trim(AData.HuellaAnterior) +
    '&FechaHoraHusoGenRegistro=' + Trim(AData.FechaHoraGenRegistro);

  // Hash SHA-256 en mayusculas (64 caracteres)
  Result := UpperCase(THashSHA2.GetHashString(LCadenaNormalizada, THashSHA2.TSHA2Version.SHA256));
end;

class function TSifCrypto.GenerarUrlQR(AModo: TSifModoOperacion;
  const ANifEmisor, ASerieNumero, AFechaExpedicion: string;
  AImporteTotal: Double; const AHuellaCompleta: string): string;
var
  LBaseUrl, LHuella8: string;
  LFormatSettings: TFormatSettings;
begin
  LFormatSettings := TFormatSettings.Create('es-ES');
  LFormatSettings.DecimalSeparator := '.';

  if AModo = sifVeriFactuProd then
    LBaseUrl := URL_VERIFICACION_QR_PROD
  else
    LBaseUrl := URL_VERIFICACION_QR_TEST;

  LHuella8 := ExtraerPrefijoHuella(AHuellaCompleta);

  Result := Format('%s?nif=%s&numserie=%s&fecha=%s&importe=%s&huella=%s', [
    LBaseUrl,
    Trim(ANifEmisor),
    Trim(ASerieNumero),
    Trim(AFechaExpedicion),
    FloatToStrF(AImporteTotal, ffFixed, 12, 2, LFormatSettings),
    LHuella8
  ]);
end;

class function TSifCrypto.ExtraerPrefijoHuella(const AHuellaCompleta: string): string;
begin
  Result := Copy(Trim(AHuellaCompleta), 1, 8);
end;

end.
