unit frm_Facturas;

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.Grids,
  Vcl.DBGrids, Data.DB, Vcl.Buttons, uAppTheme, FireDAC.Stan.Param, FireDAC.Comp.Client,
  frm_BaseList, Vcl.ComCtrls;

type
  TfrmFacturas = class(TfrmBaseList)
    cbSeries: TComboBox;
    cbPeriodo: TComboBox;
    chkSoloAsegurados: TCheckBox;
    btnExportXmlEdicom: TSpeedButton;
    btnSendWsEdicom: TSpeedButton;
    btnDocsRelacionados: TSpeedButton;

    procedure FormShow(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject); override;
    procedure btnNewClick(Sender: TObject); override;
    procedure btnEditClick(Sender: TObject); override;
    procedure btnPrintClick(Sender: TObject); override;
    procedure cbFilterChange(Sender: TObject);
    procedure dbGridMainCellClick(Column: TColumn);
    procedure btnExportXmlEdicomClick(Sender: TObject);
    procedure btnSendWsEdicomClick(Sender: TObject);
    procedure btnDocsRelacionadosClick(Sender: TObject);
  protected
    procedure ApplyAllFilters; override;
  private
  public
    { Public declarations }
  end;

var
  frmFacturas: TfrmFacturas;

implementation

uses
  dmg_Main, frm_FacturasEditor, System.DateUtils, uEdicomInvoiceExporter, uEdicomWsClient,
  uFacturaPrintService, frm_DocumentosRelacionados;

{$R *.dfm}

procedure TfrmFacturas.btnRefreshClick(Sender: TObject);
var
  LSql: string;
  LSearch: string;
  LDateStart, LDateEnd: TDateTime;
  LUseDateFilter: Boolean;
begin
  if dmgMain.CurrentCompanyId = '' then
    Exit;

  LSearch := Trim(edtSearch.Text);
  FQryMain.Close;

  LSql := 'SELECT f.id, f.serie as Serie, f.numero, f.fecha, c.nombre_fiscal as cliente, ' +
          'COALESCE(c.asegurado, 0) as asegurado, f.total, ' +
          'COALESCE(f.edi_estado_envio, ''NO_ENVIADO'') as estado_edi, f.pagada, f.fecha_cobro ' +
          'FROM ge_facturas f ' +
          'LEFT JOIN ge_clientes c ON f.cliente_id = c.id ' +
          'WHERE f.empresa_id = :empresa ';

  if LSearch <> '' then
  begin
    LSql := LSql + 'AND (c.nombre_fiscal LIKE :search OR f.numero LIKE :search) ';
  end;

  // Filtro de Serie
  if Assigned(cbSeries) and (cbSeries.ItemIndex > 0) then
    LSql := LSql + 'AND f.serie = :serie_filter ';

  // Filtro de Clientes Asegurados
  if Assigned(chkSoloAsegurados) and chkSoloAsegurados.Checked then
    LSql := LSql + 'AND COALESCE(c.asegurado, 0) = 1 ';

  // Filtro de Periodo
  LUseDateFilter := False;
  if Assigned(cbPeriodo) and (cbPeriodo.ItemIndex > 0) then
  begin
    LUseDateFilter := True;
    LDateEnd := Date;
    case cbPeriodo.ItemIndex of
      1: // Este Mes
        LDateStart := StartOfAMonth(YearOf(Date), MonthOf(Date));
      2: // Mes Anterior
        begin
          LDateStart := IncMonth(StartOfAMonth(YearOf(Date), MonthOf(Date)), -1);
          LDateEnd := EndOfAMonth(YearOf(LDateStart), MonthOf(LDateStart));
        end;
      3: // Este Trimestre
        begin
          case MonthOf(Date) of
            1..3: LDateStart := StartOfAMonth(YearOf(Date), 1);
            4..6: LDateStart := StartOfAMonth(YearOf(Date), 4);
            7..9: LDateStart := StartOfAMonth(YearOf(Date), 7);
            else LDateStart := StartOfAMonth(YearOf(Date), 10);
          end;
        end;
      4: // Este Año
        LDateStart := StartOfAMonth(YearOf(Date), 1);
      5: // Año Anterior
        begin
          LDateStart := StartOfAMonth(YearOf(Date) - 1, 1);
          LDateEnd := EncodeDate(YearOf(Date) - 1, 12, 31);
        end;
    end;
    LSql := LSql + 'AND f.fecha >= :date_start AND f.fecha <= :date_end ';
  end;

  LSql := LSql + 'ORDER BY f.fecha DESC, f.numero DESC';

  FQryMain.SQL.Text := LSql;
  FQryMain.ParamByName('empresa').AsString := dmgMain.CurrentCompanyId;

  if LSearch <> '' then
    FQryMain.ParamByName('search').AsString := '%' + LSearch + '%';

  if Assigned(cbSeries) and (cbSeries.ItemIndex > 0) then
    FQryMain.ParamByName('serie_filter').AsString := cbSeries.Text;

  if LUseDateFilter then
  begin
    FQryMain.ParamByName('date_start').AsDate := LDateStart;
    FQryMain.ParamByName('date_end').AsDate := LDateEnd;
  end;

  FQryMain.Open;
  if FQryMain.FindField('id') <> nil then
    FQryMain.FieldByName('id').Visible := False;
  if FQryMain.FindField('asegurado') <> nil then
    FQryMain.FieldByName('asegurado').DisplayLabel := 'Asegurado';
  if FQryMain.FindField('estado_edi') <> nil then
    FQryMain.FieldByName('estado_edi').DisplayLabel := 'Estado EDI';
end;

procedure TfrmFacturas.cbFilterChange(Sender: TObject);
begin
  btnRefreshClick(nil);
end;

procedure TfrmFacturas.btnEditClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if not Assigned(frmFacturasEditor) then
    Application.CreateForm(TfrmFacturasEditor, frmFacturasEditor);
  
  frmFacturasEditor.ParentForm := Self;
  frmFacturasEditor.FacturaId := FQryMain.FieldByName('id').AsString;
  frmFacturasEditor.ShowModal;
  btnRefreshClick(nil);
end;

procedure TfrmFacturas.btnNewClick(Sender: TObject);
begin
  if not Assigned(frmFacturasEditor) then
    Application.CreateForm(TfrmFacturasEditor, frmFacturasEditor);
  
  frmFacturasEditor.ParentForm := Self;
  frmFacturasEditor.FacturaId := '';
  frmFacturasEditor.ShowModal;
  btnRefreshClick(nil);
end;

procedure TfrmFacturas.btnPrintClick(Sender: TObject);
var
  LIds: TArray<string>;
begin
  LIds := GetSelectedOrCurrentIds('id');
  if Length(LIds) = 0 then
  begin
    ShowMessage('Debe seleccionar al menos una factura para imprimir.');
    Exit;
  end;

  TFacturaPrintService.ImprimirFacturas(Self, LIds, dbGridMain);
end;

procedure TfrmFacturas.FormShow(Sender: TObject);
var
  Qry: TFDQuery;
begin
  DateFilterField := 'fecha';
  StatusFilterField := 'pagada';
  StatusValues.Add('Pagada');
  StatusValues.Add('Pendiente');
  AlphaFilterField := 'cliente';

  inherited;
  dbGridMain.OnCellClick := dbGridMainCellClick;
  pnlSearch.Visible := True;
  lblSearch.Caption := 'Buscar Factura:';

  // Cargar series reales desde ge_series
  cbSeries.Items.Clear;
  cbSeries.Items.Add('Todas Series');
  cbSeries.ItemIndex := 0;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT DISTINCT serie, por_defecto FROM ge_series WHERE activo = 1 AND empresa_id = :emp ORDER BY serie';
    Qry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    Qry.Open;
    while not Qry.Eof do
    begin
      cbSeries.Items.Add(Qry.FieldByName('serie').AsString);
      if Qry.FieldByName('por_defecto').AsInteger <> 0 then
        cbSeries.ItemIndex := cbSeries.Items.Count - 1;
      Qry.Next;
    end;
  finally
    Qry.Free;
  end;

  // Refrescar para aplicar el filtro de la serie por defecto inmediatamente
  btnRefreshClick(nil);
end;

procedure TfrmFacturas.btnDocsRelacionadosClick(Sender: TObject);
begin
  if FQryMain.IsEmpty or (FQryMain.FindField('id') = nil) or (FQryMain.FieldByName('id').AsString = '') then
  begin
    ShowMessage('Debe seleccionar una factura para consultar sus documentos relacionados.');
    Exit;
  end;

  TfrmDocumentosRelacionados.MostrarDocumentos(Self, dvtFactura, FQryMain.FieldByName('id').AsString,
    FQryMain.FieldByName('serie').AsString + '/' + FQryMain.FieldByName('numero').AsString);
end;

procedure TfrmFacturas.btnExportXmlEdicomClick(Sender: TObject);
var
  LFacturaId: string;
begin
  if FQryMain.IsEmpty then Exit;

  LFacturaId := FQryMain.FieldByName('id').AsString;
  ExportarFacturaXmlEdicom(LFacturaId, dmgMain.dbConn);
end;

procedure TfrmFacturas.btnSendWsEdicomClick(Sender: TObject);
var
  LFacturaId, LErrMsg, LXmlContent, LResponse: string;
  LClient: TEdicomWsClient;
begin
  if FQryMain.IsEmpty then Exit;

  LFacturaId := FQryMain.FieldByName('id').AsString;

  // 1. Validar Datos Críticos
  if not ValidarFacturaEdicom(LFacturaId, dmgMain.dbConn, LErrMsg) then
  begin
    MessageDlg(LErrMsg, mtWarning, [mbOK], 0);
    Exit;
  end;

  // 2. Generar XML INVOIC
  LXmlContent := GenerarXmlInvoic(LFacturaId, dmgMain.dbConn);

  // 3. Enviar mediante WebService REST API (uEdicomWsClient)
  LClient := TEdicomWsClient.Create(dmgMain.dbConn, dmgMain.CurrentCompanyId);
  try
    Screen.Cursor := crHourGlass;
    try
      if LClient.PublicarFactura(LFacturaId, LXmlContent, LResponse) then
      begin
        MessageDlg('Factura ' + FQryMain.FieldByName('Serie').AsString + '-' + FQryMain.FieldByName('numero').AsString +
                   ' enviada con ÉXITO a EDICOM iPaaS.' + sLineBreak + sLineBreak +
                   'Respuesta del Servidor:' + sLineBreak + Copy(LResponse, 1, 300),
                   mtInformation, [mbOK], 0);
        btnRefreshClick(nil);
      end
      else
      begin
        MessageDlg('Error al enviar la factura a EDICOM iPaaS.' + sLineBreak + sLineBreak +
                   'Respuesta del Servidor:' + sLineBreak + Copy(LResponse, 1, 400),
                   mtError, [mbOK], 0);
        btnRefreshClick(nil);
      end;
    finally
      Screen.Cursor := crDefault;
    end;
  finally
    LClient.Free;
  end;
end;

procedure TfrmFacturas.dbGridMainCellClick(Column: TColumn);
begin
  if Assigned(Column.Field) and 
     (SameText(Column.Field.FieldName, 'pagado') or 
      SameText(Column.Field.FieldName, 'pagada')) then
  begin
    btnEditClick(nil);
  end;
end;

procedure TfrmFacturas.ApplyAllFilters;
var
  LTotal: Double;
  bm: TBookmark;
begin
  inherited ApplyAllFilters;
  
  if FQryMain.Active and (FQryMain.FindField('total') <> nil) then
  begin
    LTotal := 0;
    FQryMain.DisableControls;
    bm := FQryMain.GetBookmark;
    try
      FQryMain.First;
      while not FQryMain.Eof do
      begin
        LTotal := LTotal + FQryMain.FieldByName('total').AsFloat;
        FQryMain.Next;
      end;
    finally
      if FQryMain.BookmarkValid(bm) then
        FQryMain.GotoBookmark(bm);
      FQryMain.FreeBookmark(bm);
      FQryMain.EnableControls;
    end;
    
    lblRecordCount.Caption := lblRecordCount.Caption + ' - Importe Total: ' + FormatFloat('#,##0.00 €', LTotal);
  end;
end;

end.
