unit frm_Pedidos;

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, System.UITypes, Vcl.ComCtrls, System.Generics.Collections;

type
  TfrmPedidos = class(TfrmBaseList)
    btnImportPDF: TSpeedButton;
    btnImportWeb: TSpeedButton;
    btnImportEDI: TSpeedButton;
    btnDownloadWsEdicom: TSpeedButton;
    btnDocsRelacionados: TSpeedButton;
    btnPasarAProduccion: TSpeedButton;
    lblFiltroEmpresa: TLabel;
    cbbFiltroEmpresa: TComboBox;

    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject); override;
    procedure btnNewClick(Sender: TObject); override;
    procedure btnEditClick(Sender: TObject); override;
    procedure btnDeleteClick(Sender: TObject); override;
    procedure btnImportEDIClick(Sender: TObject);
    procedure btnImportWebClick(Sender: TObject);
    procedure btnImportPDFClick(Sender: TObject);
    procedure btnDownloadWsEdicomClick(Sender: TObject);
    procedure btnDocsRelacionadosClick(Sender: TObject);
    procedure btnPasarAProduccionClick(Sender: TObject);
    procedure btnPrintClick(Sender: TObject); override;
    procedure cbbFiltroEmpresaChange(Sender: TObject);
  private
    procedure DateFilterRequery(Sender: TObject);
    procedure CargarComboEmpresas;
  public
    { Public declarations }
  end;

var
  frmPedidos: TfrmPedidos;

implementation

uses
  dmg_Main, frm_PedidosEditor, uDbErrorHandler, uEdicomProcessor, frm_ImportarPedidosWeb, uEnviosWebImporter, uIAService, System.JSON, uEdicomWsClient,
  frm_DocumentosRelacionados, uPedidoPrintService, frm_OrdenProduccionEditor;

{$R *.dfm}

procedure TfrmPedidos.FormShow(Sender: TObject);
begin
  AlphaFilterField := 'cliente_nombre';
  StatusFilterField := 'estado';
  StatusValues.Clear;
  StatusValues.Add('PENDIENTE');
  StatusValues.Add('EN PREPARACION');
  StatusValues.Add('PROCESADO');
  StatusValues.Add('PREPARADO');
  StatusValues.Add('ENVIADO');
  StatusValues.Add('CANCELADO');
  StatusValues.Add('COMPLETADO');
  StatusValues.Add('RECHAZADO');
  StatusValues.Add('ERROR');
  DateFilterField := 'fecha_pedido';
  rbDateMonth.Checked := True;

  CargarComboEmpresas;

  inherited;
  frmPedidos := Self;
  OnDestroy := FormDestroy;
  // Ocultar componentes heredados innecesarios
  pnlSearch.Visible := True;
  lblSearch.Caption := 'Buscar Pedido:';

  if Assigned(cbbFiltroEmpresa) then
    cbbFiltroEmpresa.OnChange := cbbFiltroEmpresaChange;

  // Reasignar eventos de filtrado de fecha para forzar reconsulta al servidor
  // (el filtro de fecha se aplica en la SQL, no solo client-side)
  rbDateAll.OnClick := DateFilterRequery;
  rbDateToday.OnClick := DateFilterRequery;
  rbDateWeek.OnClick := DateFilterRequery;
  rbDateMonth.OnClick := DateFilterRequery;
  rbDateCustom.OnClick := DateFilterRequery;
  dtpStart.OnChange := DateFilterRequery;
  dtpEnd.OnChange := DateFilterRequery;
end;

procedure TfrmPedidos.CargarComboEmpresas;
var
  Qry: TFDQuery;
  LEmpresaActualId: Integer;
  I: Integer;
begin
  if not Assigned(cbbFiltroEmpresa) then Exit;

  cbbFiltroEmpresa.Items.Clear;
  cbbFiltroEmpresa.Items.AddObject('-- Todas las Empresas --', TObject(IntPtr(0)));

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

  LEmpresaActualId := StrToIntDef(dmgMain.CurrentCompanyId, 1);
  cbbFiltroEmpresa.ItemIndex := 0;
  for I := 1 to cbbFiltroEmpresa.Items.Count - 1 do
  begin
    if Integer(IntPtr(cbbFiltroEmpresa.Items.Objects[I])) = LEmpresaActualId then
    begin
      cbbFiltroEmpresa.ItemIndex := I;
      Break;
    end;
  end;
end;

procedure TfrmPedidos.cbbFiltroEmpresaChange(Sender: TObject);
begin
  btnRefreshClick(nil);
end;

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

  TfrmDocumentosRelacionados.MostrarDocumentos(Self, dvtPedido, FQryMain.FieldByName('id').AsString,
    FQryMain.FieldByName('numero_pedido').AsString);
end;

function SeleccionarOrdenProduccion(AOwner: TComponent; AQryOrders: TFDQuery): Integer;
var
  LDlg: TForm;
  LCombo: TComboBox;
  LLabel: TLabel;
  LBtnOk, LBtnCancel: TButton;
begin
  Result := 0;
  if AQryOrders.IsEmpty then Exit;

  if AQryOrders.RecordCount = 1 then
  begin
    AQryOrders.First;
    Result := AQryOrders.FieldByName('id').AsInteger;
    Exit;
  end;

  LDlg := TForm.CreateNew(AOwner);
  try
    LDlg.Caption := 'Seleccionar Orden de Producción';
    LDlg.ClientWidth := 460;
    LDlg.ClientHeight := 140;
    LDlg.Position := poScreenCenter;
    LDlg.BorderStyle := bsDialog;

    LLabel := TLabel.Create(LDlg);
    LLabel.Parent := LDlg;
    LLabel.SetBounds(24, 16, 412, 20);
    LLabel.Caption := 'Este pedido tiene varias órdenes. Seleccione la que desea abrir:';

    LCombo := TComboBox.Create(LDlg);
    LCombo.Parent := LDlg;
    LCombo.SetBounds(24, 42, 412, 26);
    LCombo.Style := csDropDownList;

    AQryOrders.First;
    while not AQryOrders.Eof do
    begin
      LCombo.Items.AddObject(
        Format('%s (%s) — %s', [
          AQryOrders.FieldByName('numero_orden').AsString,
          AQryOrders.FieldByName('obrador_nombre').AsString,
          FormatDateTime('dd/MM/yyyy', AQryOrders.FieldByName('fecha_produccion').AsDateTime)
        ]),
        TObject(IntPtr(AQryOrders.FieldByName('id').AsInteger))
      );
      AQryOrders.Next;
    end;
    LCombo.ItemIndex := 0;

    LBtnOk := TButton.Create(LDlg);
    LBtnOk.Parent := LDlg;
    LBtnOk.SetBounds(246, 88, 90, 30);
    LBtnOk.Caption := 'Abrir';
    LBtnOk.ModalResult := mrOk;
    LBtnOk.Default := True;

    LBtnCancel := TButton.Create(LDlg);
    LBtnCancel.Parent := LDlg;
    LBtnCancel.SetBounds(346, 88, 90, 30);
    LBtnCancel.Caption := 'Cancelar';
    LBtnCancel.ModalResult := mrCancel;
    LBtnCancel.Cancel := True;

    if LDlg.ShowModal = mrOk then
    begin
      if LCombo.ItemIndex >= 0 then
        Result := Integer(IntPtr(LCombo.Items.Objects[LCombo.ItemIndex]));
    end;
  finally
    LDlg.Free;
  end;
end;

procedure TfrmPedidos.btnPasarAProduccionClick(Sender: TObject);
var
  LPedidoId: Integer;
  LNumeroPedido: string;
  LEmpresaId: Integer;
  LQryOrders: TFDQuery;
  LQryCounts: TFDQuery;
  LFrmOrden: TfrmOrdenProduccionEditor;
  LOrdersSummary: string;
  LSelectedOrdenId: Integer;
  LTotalLineas, LLineasAsignadas, LLineasPendientes: Integer;
  LPromptMsg: string;
  LDlgResult: Integer;
begin
  if FQryMain.IsEmpty or (FQryMain.FindField('id') = nil) then Exit;

  LPedidoId := FQryMain.FieldByName('id').AsInteger;
  if LPedidoId <= 0 then Exit;

  LNumeroPedido := FQryMain.FieldByName('numero_pedido').AsString;
  LEmpresaId := FQryMain.FieldByName('empresa_id').AsInteger;

  // 1. Obtener órdenes de producción existentes vinculadas a este pedido
  LQryOrders := TFDQuery.Create(nil);
  try
    LQryOrders.Connection := dmgMain.dbConn;
    LQryOrders.SQL.Text :=
      'SELECT op.id, op.numero_orden, op.fecha_produccion, ' +
      '       COALESCE(obr.nombre, ''Sin Obrador'') AS obrador_nombre ' +
      'FROM ge_ordenes_produccion op ' +
      'LEFT JOIN ge_obradores obr ON op.id_obrador = obr.id ' +
      'WHERE op.id_pedido = :id_pedido ' +
      'ORDER BY op.id ASC';
    LQryOrders.ParamByName('id_pedido').AsInteger := LPedidoId;
    LQryOrders.Open;

    // 2. Contar líneas totales del pedido y líneas ya asignadas a producción
    LTotalLineas := 0;
    LLineasAsignadas := 0;
    LQryCounts := TFDQuery.Create(nil);
    try
      LQryCounts.Connection := dmgMain.dbConn;
      LQryCounts.SQL.Text :=
        'SELECT ' +
        '  COUNT(pl.id) AS total_lineas, ' +
        '  COUNT(DISTINCT opl.id_pedido_linea) AS lineas_asignadas ' +
        'FROM ge_pedidos_lineas pl ' +
        'LEFT JOIN ( ' +
        '  SELECT opl_sub.id_pedido_linea ' +
        '  FROM ge_ordenes_produccion_lineas opl_sub ' +
        '  JOIN ge_ordenes_produccion op_sub ON op_sub.id = opl_sub.id_orden ' +
        '  WHERE opl_sub.id_pedido_linea IS NOT NULL ' +
        ') opl ON opl.id_pedido_linea = pl.id ' +
        'WHERE pl.id_pedido = :id_pedido';
      LQryCounts.ParamByName('id_pedido').AsInteger := LPedidoId;
      LQryCounts.Open;
      if not LQryCounts.IsEmpty then
      begin
        LTotalLineas := LQryCounts.FieldByName('total_lineas').AsInteger;
        LLineasAsignadas := LQryCounts.FieldByName('lineas_asignadas').AsInteger;
      end;
    finally
      LQryCounts.Free;
    end;

    LLineasPendientes := LTotalLineas - LLineasAsignadas;
    if LLineasPendientes < 0 then LLineasPendientes := 0;

    // 3. Evaluar flujo según si ya existen órdenes o no
    if not LQryOrders.IsEmpty then
    begin
      LOrdersSummary := '';
      LQryOrders.First;
      while not LQryOrders.Eof do
      begin
        LOrdersSummary := LOrdersSummary + Format('  • %s (%s) — Fecha: %s' + sLineBreak, [
          LQryOrders.FieldByName('numero_orden').AsString,
          LQryOrders.FieldByName('obrador_nombre').AsString,
          FormatDateTime('dd/MM/yyyy', LQryOrders.FieldByName('fecha_produccion').AsDateTime)
        ]);
        LQryOrders.Next;
      end;

      if LLineasPendientes > 0 then
      begin
        LPromptMsg :=
          Format('El pedido %s ya cuenta con orden(es) de producción generada(s):' + sLineBreak +
                 '%s' + sLineBreak +
                 'Líneas pendientes de asignar a obrador: %d de %d.' + sLineBreak + sLineBreak +
                 '¿Desea crear una NUEVA orden de producción para otro obrador con las líneas restantes?' + sLineBreak + sLineBreak +
                 '• [SÍ]: Crear nueva orden (se precargarán los %d artículos pendientes).' + sLineBreak +
                 '• [NO]: Abrir orden de producción existente.' + sLineBreak +
                 '• [CANCELAR]: Volver al listado.',
                 [LNumeroPedido, LOrdersSummary, LLineasPendientes, LTotalLineas, LLineasPendientes]);

        LDlgResult := MessageDlg(LPromptMsg, mtConfirmation, [mbYes, mbNo, mbCancel], 0);

        if LDlgResult = mrCancel then
          Exit
        else if LDlgResult = mrNo then
        begin
          LSelectedOrdenId := SeleccionarOrdenProduccion(Self, LQryOrders);
          if LSelectedOrdenId > 0 then
          begin
            LFrmOrden := TfrmOrdenProduccionEditor.Create(Application);
            try
              LFrmOrden.OrdenId := LSelectedOrdenId;
              LFrmOrden.ShowModal;
            finally
              LFrmOrden.Free;
            end;
          end;
          Exit;
        end;
      end
      else
      begin
        // Todas las líneas ya están asignadas
        LPromptMsg :=
          Format('Todas las líneas del pedido %s ya han sido asignadas a producción:' + sLineBreak +
                 '%s' + sLineBreak +
                 '¿Qué desea hacer?' + sLineBreak + sLineBreak +
                 '• [SÍ]: Abrir una orden existente para consultarla o editarla.' + sLineBreak +
                 '• [NO]: Crear una orden de producción ADICIONAL (para duplicar o fabricar extra).' + sLineBreak +
                 '• [CANCELAR]: Volver al listado.',
                 [LNumeroPedido, LOrdersSummary]);

        LDlgResult := MessageDlg(LPromptMsg, mtConfirmation, [mbYes, mbNo, mbCancel], 0);

        if LDlgResult = mrCancel then
          Exit
        else if LDlgResult = mrYes then
        begin
          LSelectedOrdenId := SeleccionarOrdenProduccion(Self, LQryOrders);
          if LSelectedOrdenId > 0 then
          begin
            LFrmOrden := TfrmOrdenProduccionEditor.Create(Application);
            try
              LFrmOrden.OrdenId := LSelectedOrdenId;
              LFrmOrden.ShowModal;
            finally
              LFrmOrden.Free;
            end;
          end;
          Exit;
        end;
      end;
    end;

    // 4. Crear nueva orden de producción vinculada al pedido
    LFrmOrden := TfrmOrdenProduccionEditor.Create(Application);
    try
      LFrmOrden.InitialEmpresaId := LEmpresaId;
      LFrmOrden.InitialPedidoId := LPedidoId;
      if LFrmOrden.ShowModal = mrOk then
      begin
        // Si el pedido estaba PENDIENTE o PROCESADO, avanzar estado a EN PREPARACION
        if (FQryMain.FindField('estado') <> nil) and
           ((FQryMain.FieldByName('estado').AsString = 'PENDIENTE') or
            (FQryMain.FieldByName('estado').AsString = 'PROCESADO') or
            (FQryMain.FieldByName('estado').AsString = '')) then
        begin
          try
            dmgMain.dbConn.ExecSQL(
              'UPDATE ge_pedidos SET estado = ''EN PREPARACION'' WHERE id = :id',
              [LPedidoId]
            );
          except
            // Continuar aunque falle el update de estado
          end;
        end;

        // Refrescar lista manteniendo posición en el registro
        btnRefreshClick(nil);
        FQryMain.Locate('id', LPedidoId, []);
        ShowMessage(Format('El pedido %s se ha pasado a producción correctamente.', [LNumeroPedido]));
      end;
    finally
      LFrmOrden.Free;
    end;
  finally
    LQryOrders.Free;
  end;
end;

procedure TfrmPedidos.FormDestroy(Sender: TObject);
begin
  if frmPedidos = Self then
    frmPedidos := nil;
end;

procedure TfrmPedidos.btnRefreshClick(Sender: TObject);
var
  LSql: string;
  LSelectedEmpresaId: Integer;
begin
  LSelectedEmpresaId := 0;
  if Assigned(cbbFiltroEmpresa) and (cbbFiltroEmpresa.ItemIndex > 0) then
    LSelectedEmpresaId := Integer(IntPtr(cbbFiltroEmpresa.Items.Objects[cbbFiltroEmpresa.ItemIndex]));

  FQryMain.Close;
  LSql := 'SELECT p.id, p.empresa_id, p.numero_pedido, p.fecha_pedido, p.fecha_entrega, ' +
          '  emp.nombre as empresa_nombre, ' +
          '  COALESCE(NULLIF(c.nombre_comercial, ''''), c.nombre_fiscal) as cliente_nombre, ' +
          '  d.descripcion as destino_nombre, ' +
          '  p.total, p.estado, ' +
          '  COALESCE(lin.total_unidades, 0) as total_unidades, ' +
          '  COALESCE(lin.total_bultos, 0) as total_bultos, ' +
          '  CASE WHEN alb.numero IS NOT NULL THEN CONCAT(COALESCE(alb.serie, ''''), ''/'', alb.numero) ELSE ''—'' END as albaran_num, ' +
          '  CASE WHEN fac.numero IS NOT NULL THEN CONCAT(COALESCE(fac.serie, ''''), ''/'', fac.numero) ELSE ''—'' END as factura_num, ' +
          '  CAST(CASE WHEN op.numero_orden IS NOT NULL AND op.numero_orden <> '''' THEN op.numero_orden ELSE ''—'' END AS CHAR(255)) as orden_prod_num ' +
          'FROM ge_pedidos p ' +
          'JOIN ge_empresas emp ON p.empresa_id = emp.id ' +
          'JOIN ge_clientes c ON p.cliente_id = c.id ' +
          'LEFT JOIN ge_clientes_destinos d ON p.clientedestino_id = d.id ' +
          'LEFT JOIN ( ' +
          '  SELECT id_pedido, SUM(cantidad) as total_unidades, SUM(bultos) as total_bultos ' +
          '  FROM ge_pedidos_lineas GROUP BY id_pedido ' +
          ') lin ON lin.id_pedido = p.id ' +
          'LEFT JOIN ge_albaranes alb ON alb.id_pedido = p.id ' +
          'LEFT JOIN ge_facturas fac ON alb.id_factura = fac.id ' +
          'LEFT JOIN ( ' +
          '  SELECT COALESCE(op_sub.id_pedido, pl_sub.id_pedido) as id_pedido, ' +
          '         CAST(GROUP_CONCAT(DISTINCT ' +
          '           CONCAT(op_sub.numero_orden, ' +
          '             CASE WHEN obr.nombre IS NOT NULL AND obr.nombre <> '''' ' +
          '                  THEN CONCAT('' ('', obr.nombre, '')'') ' +
          '                  ELSE '''' ' +
          '             END' +
          '           ) SEPARATOR '', '' ' +
          '         ) AS CHAR(255)) as numero_orden ' +
          '  FROM ge_ordenes_produccion op_sub ' +
          '  LEFT JOIN ge_obradores obr ON op_sub.id_obrador = obr.id ' +
          '  LEFT JOIN ge_ordenes_produccion_lineas opl_sub ON opl_sub.id_orden = op_sub.id ' +
          '  LEFT JOIN ge_pedidos_lineas pl_sub ON pl_sub.id = opl_sub.id_pedido_linea ' +
          '  WHERE op_sub.id_pedido IS NOT NULL OR pl_sub.id_pedido IS NOT NULL ' +
          '  GROUP BY COALESCE(op_sub.id_pedido, pl_sub.id_pedido) ' +
          ') op ON op.id_pedido = p.id ' +
          'WHERE 1=1 ';
  
  if LSelectedEmpresaId > 0 then
    LSql := LSql + 'AND p.empresa_id = :empresa ';

  if Trim(edtSearch.Text) <> '' then
  begin
    LSql := LSql + 'AND (p.numero_pedido LIKE :search ' +
                   'OR c.nombre_comercial LIKE :search ' +
                   'OR c.nombre_fiscal LIKE :search ' +
                   'OR emp.nombre LIKE :search ' +
                   'OR alb.numero LIKE :search ' +
                   'OR fac.numero LIKE :search ' +
                   'OR op.numero_orden LIKE :search) ';
  end;
  // Filtro de fechas a nivel de servidor para minimizar transferencia en remoto
  if rbDateToday.Checked then
    LSql := LSql + 'AND p.fecha_pedido >= :fecha_desde '
  else if rbDateWeek.Checked then
    LSql := LSql + 'AND p.fecha_pedido >= :fecha_desde '
  else if rbDateMonth.Checked then
    LSql := LSql + 'AND p.fecha_pedido >= :fecha_desde '
  else if rbDateCustom.Checked then
    LSql := LSql + 'AND p.fecha_pedido >= :fecha_desde AND p.fecha_pedido < :fecha_hasta ';

  LSql := LSql + 'ORDER BY p.fecha_pedido DESC, p.numero_pedido DESC';
  
  FQryMain.SQL.Text := LSql;
  if LSelectedEmpresaId > 0 then
    FQryMain.ParamByName('empresa').AsInteger := LSelectedEmpresaId;
  
  if Trim(edtSearch.Text) <> '' then
    FQryMain.ParamByName('search').AsString := '%' + Trim(edtSearch.Text) + '%';

  // Asignar parámetros de fecha según el filtro temporal seleccionado
  if rbDateToday.Checked then
    FQryMain.ParamByName('fecha_desde').AsDateTime := Date
  else if rbDateWeek.Checked then
    FQryMain.ParamByName('fecha_desde').AsDateTime := Date - 7
  else if rbDateMonth.Checked then
    FQryMain.ParamByName('fecha_desde').AsDateTime := IncMonth(Date, -1)
  else if rbDateCustom.Checked then
  begin
    FQryMain.ParamByName('fecha_desde').AsDateTime := Trunc(dtpStart.Date);
    FQryMain.ParamByName('fecha_hasta').AsDateTime := Trunc(dtpEnd.Date) + 1;
  end;
    
  FQryMain.Open;
  
  dbGridMain.Columns.Clear;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'empresa_nombre';
    Title.Caption := 'Empresa';
    Width := 130;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'numero_pedido';
    Title.Caption := 'Nº Pedido';
    Width := 115;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'fecha_pedido';
    Title.Caption := 'Fecha Pedido';
    Width := 95;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'fecha_entrega';
    Title.Caption := 'Fecha Entrega';
    Width := 95;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'cliente_nombre';
    Title.Caption := 'Cliente';
    Width := 230;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'destino_nombre';
    Title.Caption := 'Destino';
    Width := 160;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'albaran_num';
    Title.Caption := 'Nº Albarán';
    Width := 90;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'factura_num';
    Title.Caption := 'Nº Factura';
    Width := 90;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'orden_prod_num';
    Title.Caption := 'O. Prod.';
    Width := 180;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'total_unidades';
    Title.Caption := 'Unidades';
    Width := 75;
    Alignment := taRightJustify;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'total_bultos';
    Title.Caption := 'Bultos';
    Width := 70;
    Alignment := taRightJustify;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'total';
    Title.Caption := 'Total';
    Width := 90;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'estado';
    Title.Caption := 'Estado';
    Width := 100;
  end;
end;

procedure TfrmPedidos.DateFilterRequery(Sender: TObject);
begin
  if (Sender = dtpStart) or (Sender = dtpEnd) then
    rbDateCustom.Checked := True;
  btnRefreshClick(nil);
end;

procedure TfrmPedidos.btnEditClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if not Assigned(frmPedidosEditor) then
    Application.CreateForm(TfrmPedidosEditor, frmPedidosEditor);
  
  frmPedidosEditor.ParentForm := Self;
  frmPedidosEditor.PedidoId := FQryMain.FieldByName('id').AsString;
  if frmPedidosEditor.ShowModal = mrOk then
    btnRefreshClick(nil);
end;

procedure TfrmPedidos.btnNewClick(Sender: TObject);
begin
  if not Assigned(frmPedidosEditor) then
    Application.CreateForm(TfrmPedidosEditor, frmPedidosEditor);
  
  frmPedidosEditor.ParentForm := Self;
  frmPedidosEditor.PedidoId := '';
  if Assigned(cbbFiltroEmpresa) and (cbbFiltroEmpresa.ItemIndex > 0) then
    frmPedidosEditor.SuggestedEmpresaId := Integer(IntPtr(cbbFiltroEmpresa.Items.Objects[cbbFiltroEmpresa.ItemIndex]))
  else
    frmPedidosEditor.SuggestedEmpresaId := StrToIntDef(dmgMain.CurrentCompanyId, 1);

  if frmPedidosEditor.ShowModal = mrOk then
    btnRefreshClick(nil);
end;

procedure TfrmPedidos.btnDeleteClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if MessageDlg('¿Está seguro de que desea eliminar este pedido?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    try
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_pedidos WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsString := FQryMain.FieldByName('id').AsString;
      dmgMain.qryExec.ExecSQL;
      btnRefreshClick(nil);
    except
      on E: Exception do
        TDbErrorHandler.HandleException(E, 'Error al eliminar el pedido');
    end;
  end;
end;

procedure TfrmPedidos.btnDownloadWsEdicomClick(Sender: TObject);
var
  LCount: Integer;
  LResponseMsg: string;
begin
  Screen.Cursor := crHourGlass;
  try
    if TEdicomWsClient.DownloadAndImportPendingOrders(dmgMain.dbConn, dmgMain.CurrentCompanyId, LCount, LResponseMsg) then
    begin
      if LCount > 0 then
      begin
        MessageDlg(Format('Se han descargado e importado %d pedido(s) EDI desde EDICOM iPaaS correctamente.', [LCount]),
                   mtInformation, [mbOK], 0);
        btnRefreshClick(nil);
      end
      else
      begin
        if LResponseMsg <> '' then
          MessageDlg('Respuesta de EDICOM iPaaS:' + sLineBreak + LResponseMsg, mtInformation, [mbOK], 0)
        else
          MessageDlg('No hay pedidos nuevos en la suscripción de EDICOM iPaaS.', mtInformation, [mbOK], 0);
      end;
    end
    else
    begin
      if LResponseMsg <> '' then
        MessageDlg('Error al consultar pedidos en EDICOM iPaaS:' + sLineBreak + LResponseMsg, mtError, [mbOK], 0)
      else
        MessageDlg('No se pudieron descargar pedidos desde EDICOM iPaaS.', mtWarning, [mbOK], 0);
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

procedure TfrmPedidos.btnImportEDIClick(Sender: TObject);
var
  LOpenDlg: TOpenDialog;
  LErrorMsg: string;
  LCount: Integer;
begin
  LOpenDlg := TOpenDialog.Create(Self);
  try
    LOpenDlg.Title := 'Seleccionar archivo de pedidos de EDICOM (EDIFACT / XML)';
    LOpenDlg.Filter := 'Archivos de Pedidos EDI (*.edi;*.txt;*.xml)|*.edi;*.txt;*.xml|Archivos EDIFACT (*.edi;*.txt)|*.edi;*.txt|Archivos XML de EDICOM (*.xml)|*.xml|Todos los archivos (*.*)|*.*';
    LOpenDlg.DefaultExt := 'edi';
    if LOpenDlg.Execute then
    begin
      try
        LCount := TEdicomProcessor.ImportOrders(LOpenDlg.FileName, StrToIntDef(dmgMain.CurrentCompanyId, 1), LErrorMsg);
        if LCount > 0 then
        begin
          MessageDlg(Format('Se han importado %d pedido(s) correctamente.', [LCount]), mtInformation, [mbOK], 0);
          btnRefreshClick(nil);
        end
        else
        begin
          if LErrorMsg <> '' then
            MessageDlg('Error al importar pedidos: ' + LErrorMsg, mtError, [mbOK], 0)
          else
            MessageDlg('No se ha podido importar ningún pedido del archivo.', mtWarning, [mbOK], 0);
        end;
      except
        on E: Exception do
          MessageDlg('Excepción al procesar el archivo EDI: ' + E.Message, mtError, [mbOK], 0);
      end;
    end;
  finally
    LOpenDlg.Free;
  end;
end;

procedure TfrmPedidos.btnImportWebClick(Sender: TObject);
var
  LOrders: TArray<TWebOrderHeader>;
  LMsg, LFechaStr: string;
  i, LPendingCount, LImportadosCount: Integer;
  LDetails: TWebOrder;
  LNewId: Int64;
  LRes: Integer;
  LCompanyId: Integer;
  LFechaDesde: TDateTime;
begin
  LCompanyId := StrToIntDef(dmgMain.CurrentCompanyId, 1);
  if not TWebOrderImporter.HasWebConfig(LCompanyId) then
    Exit;

  LFechaStr := FormatDateTime('dd/MM/yyyy', Date - 30);
  if not InputQuery('Importación de Pedidos WooCommerce', 
                    'Indique la fecha desde la cual desea consultar pedidos web:', 
                    LFechaStr) then
    Exit;

  if not TryStrToDate(LFechaStr, LFechaDesde) then
    LFechaDesde := Date - 30;

  Screen.Cursor := crHourGlass;
  try
    if not TWebOrderImporter.FetchPendingOrders(LCompanyId, LFechaDesde, LOrders, LMsg) then
    begin
      Screen.Cursor := crDefault;
      MessageDlg('Error al conectar con la web: ' + LMsg, mtError, [mbOK], 0);
      Exit;
    end;
    
    LPendingCount := 0;
    for i := 0 to Length(LOrders) - 1 do
      if not LOrders[i].Importado then
        Inc(LPendingCount);
        
    Screen.Cursor := crDefault;
    
    if LPendingCount = 0 then
    begin
      MessageDlg('No hay nuevos pedidos en la web pendientes de importar.', mtInformation, [mbOK], 0);
      Exit;
    end;
    
    LRes := MessageDlg(
      Format('Se han detectado %d pedidos nuevos en la web.' + #13#10 +
             '¿Desea importarlos TODOS automáticamente ahora?' + #13#10 +
             '(Pulse "No" para abrir el importador interactivo y seleccionarlos manualmente)', 
             [LPendingCount]), 
      mtConfirmation, [mbYes, mbNo, mbCancel], 0
    );
    
    if LRes = mrCancel then Exit;
    
    if LRes = mrYes then
    begin
      Screen.Cursor := crHourGlass;
      LImportadosCount := 0;
      try
        for i := 0 to Length(LOrders) - 1 do
        begin
          if not LOrders[i].Importado then
          begin
            if TWebOrderImporter.FetchOrderDetails(StrToIntDef(dmgMain.CurrentCompanyId, 1), LOrders[i].OrderId, LDetails, LMsg) then
            begin
              LNewId := TWebOrderImporter.ImportOrder(LDetails, StrToIntDef(dmgMain.CurrentCompanyId, 1), LMsg);
              if LNewId > 0 then
                Inc(LImportadosCount);
            end;
          end;
        end;
      finally
        Screen.Cursor := crDefault;
      end;
      
      MessageDlg(Format('Se han importado %d pedido(s) correctamente.', [LImportadosCount]), mtInformation, [mbOK], 0);
      btnRefreshClick(nil);
    end
    else if LRes = mrNo then
    begin
      // Abrir importador interactivo
      if not Assigned(frmImportarPedidosWeb) then
        Application.CreateForm(TfrmImportarPedidosWeb, frmImportarPedidosWeb);
      
      frmImportarPedidosWeb.DesactivarCheckPorDefecto := True;
      if frmImportarPedidosWeb.ShowModal = mrOk then
        btnRefreshClick(nil);
    end;
  except
    on E: Exception do
    begin
      Screen.Cursor := crDefault;
      MessageDlg('Error: ' + E.Message, mtError, [mbOK], 0);
    end;
  end;
end;

procedure TfrmPedidos.btnImportPDFClick(Sender: TObject);
var
  LOpenDlg: TOpenDialog;
  LIAService: TIAService;
  LOrderJson: string;
  LErrorMsg: string;
  LForm: TForm;
  LMemo: TMemo;
  LPanel: TPanel;
  LBtnOk, LBtnCancel: TButton;
  LJSONObj: TJSONObject;
  LConfirmado: Boolean;
begin
  LOpenDlg := TOpenDialog.Create(Self);
  try
    LOpenDlg.Title := 'Seleccionar pedido de cliente en PDF (IA)';
    LOpenDlg.Filter := 'Documentos PDF (*.pdf)|*.pdf|Todos los archivos (*.*)|*.*';
    LOpenDlg.DefaultExt := 'pdf';
    
    if LOpenDlg.Execute then
    begin
      Screen.Cursor := crHourGlass;
      LIAService := TIAService.Create;
      try
        if LIAService.ProcessPdfOrder(LOpenDlg.FileName, LOrderJson, LErrorMsg) then
        begin
          Screen.Cursor := crDefault;
          
          // Mostrar el contenido extraído en un formulario modal para confirmar
          LConfirmado := False;
          LForm := TForm.Create(Self);
          try
            LForm.Caption := 'Pedido Extraído del PDF por IA - Confirmación';
            LForm.Width := 650;
            LForm.Height := 480;
            LForm.Position := poOwnerFormCenter;
            LForm.BorderStyle := bsDialog;
            
            LMemo := TMemo.Create(LForm);
            LMemo.Parent := LForm;
            LMemo.Align := alClient;
            LMemo.ScrollBars := ssVertical;
            LMemo.ReadOnly := True;
            LMemo.Font.Name := 'Consolas';
            LMemo.Font.Size := 10;
            
            // Parsear JSON para mostrar una previsualización muy limpia y legible
            LJSONObj := TJSONObject.ParseJSONValue(LOrderJson) as TJSONObject;
            if Assigned(LJSONObj) then
            begin
              try
                var LSummary := TStringBuilder.Create;
                try
                  LSummary.AppendLine('==================================================');
                  LSummary.AppendLine('       RESUMEN DE PEDIDO EXTRAÍDO POR IA');
                  LSummary.AppendLine('==================================================');
                  if LJSONObj.GetValue('order_number') <> nil then
                    LSummary.AppendLine('Nº Pedido:        ' + LJSONObj.GetValue('order_number').Value);
                  if LJSONObj.GetValue('date') <> nil then
                    LSummary.AppendLine('Fecha Pedido:     ' + LJSONObj.GetValue('date').Value);
                  if LJSONObj.GetValue('delivery_date') <> nil then
                    LSummary.AppendLine('Fecha Entrega:    ' + LJSONObj.GetValue('delivery_date').Value);
                  if LJSONObj.GetValue('client_ean') <> nil then
                    LSummary.AppendLine('EAN Cliente:      ' + LJSONObj.GetValue('client_ean').Value);
                  
                  LSummary.AppendLine;
                  LSummary.AppendLine('DETALLE DE ARTÍCULOS:');
                  LSummary.AppendLine('--------------------------------------------------');
                  
                  var LLines := LJSONObj.GetValue('lines') as TJSONArray;
                  if Assigned(LLines) then
                  begin
                    for var J := 0 to LLines.Count - 1 do
                    begin
                      var LLine := LLines.Items[J] as TJSONObject;
                      var LEan := '';
                      var LDesc := '';
                      var LCant := '';
                      var LPre := '';
                      
                      if LLine.GetValue('ean') <> nil then LEan := LLine.GetValue('ean').Value;
                      if LLine.GetValue('description') <> nil then LDesc := LLine.GetValue('description').Value;
                      if LLine.GetValue('quantity') <> nil then LCant := LLine.GetValue('quantity').Value;
                      if LLine.GetValue('price') <> nil then LPre := LLine.GetValue('price').Value;
                      
                      LSummary.AppendLine(Format('%d. EAN: %s', [J + 1, LEan]));
                      LSummary.AppendLine('   Descripción: ' + LDesc);
                      LSummary.AppendLine(Format('   Cantidad: %s  |  Precio Unitario: %s €', [LCant, LPre]));
                      LSummary.AppendLine('--------------------------------------------------');
                    end;
                  end;
                  
                  LMemo.Text := LSummary.ToString;
                finally
                  LSummary.Free;
                end;
              finally
                LJSONObj.Free;
              end;
            end
            else
              LMemo.Text := LOrderJson;
              
            LPanel := TPanel.Create(LForm);
            LPanel.Parent := LForm;
            LPanel.Align := alBottom;
            LPanel.Height := 50;
            LPanel.BevelOuter := bvNone;
            
            LBtnOk := TButton.Create(LForm);
            LBtnOk.Parent := LPanel;
            LBtnOk.Caption := 'Importar Pedido';
            LBtnOk.ModalResult := mrOk;
            LBtnOk.Default := True;
            LBtnOk.Width := 130;
            LBtnOk.Height := 30;
            LBtnOk.Left := LPanel.Width - 250;
            LBtnOk.Top := 10;
            
            LBtnCancel := TButton.Create(LForm);
            LBtnCancel.Parent := LPanel;
            LBtnCancel.Caption := 'Cancelar';
            LBtnCancel.ModalResult := mrCancel;
            LBtnCancel.Cancel := True;
            LBtnCancel.Width := 90;
            LBtnCancel.Height := 30;
            LBtnCancel.Left := LPanel.Width - 110;
            LBtnCancel.Top := 10;
            
            if LForm.ShowModal = mrOk then
              LConfirmado := True;
          finally
            LForm.Free;
          end;
          
          if LConfirmado then
          begin
            Screen.Cursor := crHourGlass;
            if TEdicomProcessor.ImportOrderFromJson(LOrderJson, StrToIntDef(dmgMain.CurrentCompanyId, 1), LErrorMsg) then
            begin
              Screen.Cursor := crDefault;
              MessageDlg('Pedido importado correctamente por IA desde el PDF.', mtInformation, [mbOK], 0);
              btnRefreshClick(nil);
            end
            else
            begin
              Screen.Cursor := crDefault;
              MessageDlg('Error al registrar el pedido procesado: ' + LErrorMsg, mtError, [mbOK], 0);
            end;
          end;
        end
        else
        begin
          Screen.Cursor := crDefault;
          MessageDlg('Error de IA al analizar el PDF: ' + LErrorMsg, mtError, [mbOK], 0);
        end;
      finally
        LIAService.Free;
      end;
    end;
  finally
    LOpenDlg.Free;
  end;
end;

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

  TPedidoPrintService.ImprimirPedidos(Self, LIds, dbGridMain);
end;

end.
