unit frm_LotesProcesosEditor;

interface

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

type
  TfrmLotesProcesosEditor = class(TForm)
    pnlTop: TPanel;
    lblTitle: TLabel;
    pnlClient: TPanel;
    pnlBottom: TPanel;
    btnGuardar: TBitBtn;
    btnCancelar: TBitBtn;
    lblLote: TLabel;
    cbbLote: TComboBox;
    lblEquipo: TLabel;
    cbbEquipo: TComboBox;
    lblTipoProceso: TLabel;
    cbbTipoProceso: TComboBox;
    lblOperario: TLabel;
    cbbOperario: TComboBox;
    lblFechaInicio: TLabel;
    dtpFechaInicio: TDateTimePicker;
    dtpHoraInicio: TDateTimePicker;
    lblFechaFin: TLabel;
    dtpFechaFin: TDateTimePicker;
    dtpHoraFin: TDateTimePicker;
    chkEnCurso: TCheckBox;
    lblTempMin: TLabel;
    edtTempMin: TEdit;
    lblTempMax: TLabel;
    edtTempMax: TEdit;
    lblTempMed: TLabel;
    edtTempMed: TEdit;
    lblEstado: TLabel;
    cbbEstado: TComboBox;
    lblObservaciones: TLabel;
    mmoObservaciones: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure chkEnCursoClick(Sender: TObject);
    procedure cbbLoteChange(Sender: TObject);
    procedure btnGuardarClick(Sender: TObject);
    procedure btnCancelarClick(Sender: TObject);
  private
    FEditId: Integer;
    FIdEquipoFijo: Integer;
    FIdLoteFijo: Integer;
    FSelectedArticuloId: Integer;
    procedure CargarLotes;
    procedure CargarEquipos;
    procedure CargarOperarios;
    procedure CargarDatos;
  public
    property EditId: Integer read FEditId write FEditId;
    property IdEquipoFijo: Integer read FIdEquipoFijo write FIdEquipoFijo;
    property IdLoteFijo: Integer read FIdLoteFijo write FIdLoteFijo;
  end;

var
  frmLotesProcesosEditor: TfrmLotesProcesosEditor;

implementation

uses
  dmg_Main, uDbErrorHandler;

{$R *.dfm}

procedure TfrmLotesProcesosEditor.FormCreate(Sender: TObject);
begin
  FEditId := 0;
  FIdEquipoFijo := 0;
  FIdLoteFijo := 0;
  FSelectedArticuloId := 0;
end;

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

  CargarLotes;
  CargarEquipos;
  CargarOperarios;

  if FEditId > 0 then
  begin
    lblTitle.Caption := '✏️ Editar Proceso de Lote #' + IntToStr(FEditId);
    CargarDatos;
  end
  else
  begin
    lblTitle.Caption := '🔥 Iniciar Proceso de Lote en Equipo';
    cbbTipoProceso.ItemIndex := 0; // HORNEADO
    cbbEstado.ItemIndex := 0; // EN_CURSO
    dtpFechaInicio.Date := Date;
    dtpHoraInicio.Time := Time;
    dtpFechaFin.Date := Date;
    dtpHoraFin.Time := Time;
    chkEnCurso.Checked := True;
    dtpFechaFin.Enabled := False;
    dtpHoraFin.Enabled := False;
    edtTempMin.Text := '0,00';
    edtTempMax.Text := '0,00';
    edtTempMed.Text := '0,00';
    mmoObservaciones.Clear;
  end;
end;

procedure TfrmLotesProcesosEditor.chkEnCursoClick(Sender: TObject);
begin
  dtpFechaFin.Enabled := not chkEnCurso.Checked;
  dtpHoraFin.Enabled := not chkEnCurso.Checked;
  if chkEnCurso.Checked then
    cbbEstado.ItemIndex := 0
  else
    cbbEstado.ItemIndex := 1;
end;

procedure TfrmLotesProcesosEditor.cbbLoteChange(Sender: TObject);
begin
  // Extraer el id_articulo del lote seleccionado si es posible
end;

procedure TfrmLotesProcesosEditor.CargarLotes;
var
  Qry: TFDQuery;
begin
  cbbLote.Items.Clear;
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text :=
      'SELECT l.id, l.id_articulo, COALESCE(NULLIF(l.codigo_antiguo, ''''), l.codigo) AS lote_cod, ' +
      '       COALESCE(art.descripcion, ''Sin Artículo'') AS art_desc ' +
      'FROM ge_lotes l ' +
      'LEFT JOIN ge_articulos art ON l.id_articulo = art.id ' +
      'WHERE l.activo = 1 AND l.codigo <> ''0'' ' +
      'ORDER BY l.id DESC LIMIT 150';
    Qry.Open;
    while not Qry.Eof do
    begin
      var LText := Format('Lote %s — %s (Ref #%d)', [Qry.FieldByName('lote_cod').AsString, Qry.FieldByName('art_desc').AsString, Qry.FieldByName('id_articulo').AsInteger]);
      cbbLote.Items.AddObject(LText, TObject(IntPtr(Qry.FieldByName('id').AsInteger)));
      Qry.Next;
    end;

    if cbbLote.Items.Count > 0 then
    begin
      cbbLote.ItemIndex := 0;
      if FIdLoteFijo > 0 then
      begin
        for var i := 0 to cbbLote.Items.Count - 1 do
        begin
          if Integer(IntPtr(cbbLote.Items.Objects[i])) = FIdLoteFijo then
          begin
            cbbLote.ItemIndex := i;
            Break;
          end;
        end;
      end;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmLotesProcesosEditor.CargarEquipos;
var
  Qry: TFDQuery;
begin
  cbbEquipo.Items.Clear;
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text :=
      'SELECT e.id, CONCAT(e.codigo, '' - '', e.nombre, '' ('', z.nombre, '')'') AS equipo_desc ' +
      'FROM ge_zonas_equipos e ' +
      'INNER JOIN ge_centros_zonas z ON e.id_zona = z.id ' +
      'WHERE z.id_empresa = :emp AND e.activo = 1 ' +
      'ORDER BY z.nombre ASC, e.nombre ASC';
    Qry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    Qry.Open;
    while not Qry.Eof do
    begin
      cbbEquipo.Items.AddObject(Qry.FieldByName('equipo_desc').AsString, TObject(IntPtr(Qry.FieldByName('id').AsInteger)));
      Qry.Next;
    end;

    if cbbEquipo.Items.Count > 0 then
    begin
      cbbEquipo.ItemIndex := 0;
      if FIdEquipoFijo > 0 then
      begin
        for var i := 0 to cbbEquipo.Items.Count - 1 do
        begin
          if Integer(IntPtr(cbbEquipo.Items.Objects[i])) = FIdEquipoFijo then
          begin
            cbbEquipo.ItemIndex := i;
            Break;
          end;
        end;
      end;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmLotesProcesosEditor.CargarOperarios;
var
  Qry: TFDQuery;
begin
  cbbOperario.Items.Clear;
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text :=
      'SELECT id, CONCAT(nombre, '' '', COALESCE(apellidos, '''')) AS trab_desc ' +
      'FROM ge_trabajadores ' +
      'WHERE id_empresa = :emp AND activo = 1 ' +
      'ORDER BY nombre ASC';
    Qry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    Qry.Open;
    while not Qry.Eof do
    begin
      cbbOperario.Items.AddObject(Qry.FieldByName('trab_desc').AsString, TObject(IntPtr(Qry.FieldByName('id').AsInteger)));
      Qry.Next;
    end;

    if cbbOperario.Items.Count > 0 then
      cbbOperario.ItemIndex := 0;
  finally
    Qry.Free;
  end;
end;

procedure TfrmLotesProcesosEditor.CargarDatos;
var
  Qry: TFDQuery;
begin
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT * FROM ge_lotes_procesos_registro WHERE id = :id';
    Qry.ParamByName('id').AsInteger := FEditId;
    Qry.Open;

    if not Qry.IsEmpty then
    begin
      var LLoteId := Qry.FieldByName('id_lote').AsInteger;
      for var i := 0 to cbbLote.Items.Count - 1 do
      begin
        if Integer(IntPtr(cbbLote.Items.Objects[i])) = LLoteId then
        begin
          cbbLote.ItemIndex := i;
          Break;
        end;
      end;

      var LEquipoId := Qry.FieldByName('id_equipo').AsInteger;
      for var i := 0 to cbbEquipo.Items.Count - 1 do
      begin
        if Integer(IntPtr(cbbEquipo.Items.Objects[i])) = LEquipoId then
        begin
          cbbEquipo.ItemIndex := i;
          Break;
        end;
      end;

      var LTipoProc := Qry.FieldByName('tipo_proceso').AsString;
      var LIdx := cbbTipoProceso.Items.IndexOf(LTipoProc);
      if LIdx >= 0 then
        cbbTipoProceso.ItemIndex := LIdx
      else
        cbbTipoProceso.ItemIndex := 0;

      var LOperarioId := Qry.FieldByName('operario_responsable_id').AsInteger;
      for var i := 0 to cbbOperario.Items.Count - 1 do
      begin
        if Integer(IntPtr(cbbOperario.Items.Objects[i])) = LOperarioId then
        begin
          cbbOperario.ItemIndex := i;
          Break;
        end;
      end;

      var LIniDT := Qry.FieldByName('fecha_inicio').AsDateTime;
      dtpFechaInicio.Date := Trunc(LIniDT);
      dtpHoraInicio.Time := Frac(LIniDT);

      if Qry.FieldByName('fecha_fin').IsNull then
      begin
        chkEnCurso.Checked := True;
        dtpFechaFin.Enabled := False;
        dtpHoraFin.Enabled := False;
      end
      else
      begin
        chkEnCurso.Checked := False;
        dtpFechaFin.Enabled := True;
        dtpHoraFin.Enabled := True;
        var LFinDT := Qry.FieldByName('fecha_fin').AsDateTime;
        dtpFechaFin.Date := Trunc(LFinDT);
        dtpHoraFin.Time := Frac(LFinDT);
      end;

      edtTempMin.Text := FormatFloat('0.00', Qry.FieldByName('temp_minima').AsFloat);
      edtTempMax.Text := FormatFloat('0.00', Qry.FieldByName('temp_maxima').AsFloat);
      edtTempMed.Text := FormatFloat('0.00', Qry.FieldByName('temp_media').AsFloat);

      var LEst := Qry.FieldByName('estado').AsString;
      var LEstIdx := cbbEstado.Items.IndexOf(LEst);
      if LEstIdx >= 0 then
        cbbEstado.ItemIndex := LEstIdx
      else
        cbbEstado.ItemIndex := 0;

      mmoObservaciones.Text := Qry.FieldByName('observaciones').AsString;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmLotesProcesosEditor.btnGuardarClick(Sender: TObject);
var
  LLoteId, LEquipoId, LOperarioId, LArtId: Integer;
  LMinT, LMaxT, LMedT: Double;
  LFechaIni, LFechaFin: TDateTime;
  LTipoProcStr, LEstadoStr: string;
  QryArt: TFDQuery;
begin
  if cbbLote.ItemIndex < 0 then
  begin
    MessageDlg('Debe seleccionar el lote a procesar.', mtWarning, [mbOK], 0);
    cbbLote.SetFocus;
    Exit;
  end;

  if cbbEquipo.ItemIndex < 0 then
  begin
    MessageDlg('Debe seleccionar el equipo/máquina (horno, etc.).', mtWarning, [mbOK], 0);
    cbbEquipo.SetFocus;
    Exit;
  end;

  LLoteId := Integer(IntPtr(cbbLote.Items.Objects[cbbLote.ItemIndex]));
  LEquipoId := Integer(IntPtr(cbbEquipo.Items.Objects[cbbEquipo.ItemIndex]));
  
  LOperarioId := 0;
  if cbbOperario.ItemIndex >= 0 then
    LOperarioId := Integer(IntPtr(cbbOperario.Items.Objects[cbbOperario.ItemIndex]));

  // Obtener artículo del lote
  LArtId := 0;
  QryArt := TFDQuery.Create(nil);
  try
    QryArt.Connection := dmgMain.dbConn;
    QryArt.SQL.Text := 'SELECT id_articulo FROM ge_lotes WHERE id = :id';
    QryArt.ParamByName('id').AsInteger := LLoteId;
    QryArt.Open;
    if not QryArt.IsEmpty then
      LArtId := QryArt.FieldByName('id_articulo').AsInteger;
  finally
    QryArt.Free;
  end;

  LTipoProcStr := cbbTipoProceso.Text;
  if LTipoProcStr = '' then LTipoProcStr := 'HORNEADO';

  LEstadoStr := cbbEstado.Text;
  if LEstadoStr = '' then LEstadoStr := 'EN_CURSO';

  LFechaIni := Trunc(dtpFechaInicio.Date) + Frac(dtpHoraInicio.Time);
  LMinT := StrToFloatDef(Trim(StringReplace(edtTempMin.Text, ',', '.', [rfReplaceAll])), 0.0);
  LMaxT := StrToFloatDef(Trim(StringReplace(edtTempMax.Text, ',', '.', [rfReplaceAll])), 0.0);
  LMedT := StrToFloatDef(Trim(StringReplace(edtTempMed.Text, ',', '.', [rfReplaceAll])), 0.0);

  try
    dmgMain.qryExec.Close;
    if FEditId > 0 then
    begin
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_lotes_procesos_registro SET ' +
        '  id_lote = :lote, id_articulo = :art, id_equipo = :eq, tipo_proceso = :proc, ' +
        '  fecha_inicio = :f_ini, fecha_fin = :f_fin, duracion_minutos = :dur, ' +
        '  temp_minima = :t_min, temp_maxima = :t_max, temp_media = :t_med, ' +
        '  operario_responsable_id = :op, estado = :est, observaciones = :obs ' +
        'WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsInteger := FEditId;
    end
    else
    begin
      dmgMain.qryExec.SQL.Text :=
        'INSERT INTO ge_lotes_procesos_registro (id_lote, id_articulo, id_equipo, tipo_proceso, fecha_inicio, fecha_fin, duracion_minutos, temp_minima, temp_maxima, temp_media, operario_responsable_id, estado, observaciones, id_user_creator) ' +
        'VALUES (:lote, :art, :eq, :proc, :f_ini, :f_fin, :dur, :t_min, :t_max, :t_med, :op, :est, :obs, :user)';
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
    end;

    dmgMain.qryExec.ParamByName('lote').AsInteger := LLoteId;
    dmgMain.qryExec.ParamByName('art').AsInteger := LArtId;
    dmgMain.qryExec.ParamByName('eq').AsInteger := LEquipoId;
    dmgMain.qryExec.ParamByName('proc').AsString := LTipoProcStr;
    dmgMain.qryExec.ParamByName('f_ini').AsDateTime := LFechaIni;

    if chkEnCurso.Checked then
    begin
      dmgMain.qryExec.ParamByName('f_fin').Clear;
      dmgMain.qryExec.ParamByName('f_fin').DataType := ftDateTime;
      dmgMain.qryExec.ParamByName('dur').Clear;
      dmgMain.qryExec.ParamByName('dur').DataType := ftInteger;
    end
    else
    begin
      LFechaFin := Trunc(dtpFechaFin.Date) + Frac(dtpHoraFin.Time);
      dmgMain.qryExec.ParamByName('f_fin').AsDateTime := LFechaFin;
      var LDur := Round((LFechaFin - LFechaIni) * 1440);
      if LDur < 0 then LDur := 0;
      dmgMain.qryExec.ParamByName('dur').AsInteger := LDur;
    end;

    dmgMain.qryExec.ParamByName('t_min').AsFloat := LMinT;
    dmgMain.qryExec.ParamByName('t_max').AsFloat := LMaxT;
    dmgMain.qryExec.ParamByName('t_med').AsFloat := LMedT;

    if LOperarioId > 0 then
      dmgMain.qryExec.ParamByName('op').AsInteger := LOperarioId
    else
    begin
      dmgMain.qryExec.ParamByName('op').Clear;
      dmgMain.qryExec.ParamByName('op').DataType := ftInteger;
    end;

    dmgMain.qryExec.ParamByName('est').AsString := LEstadoStr;
    dmgMain.qryExec.ParamByName('obs').AsString := Trim(mmoObservaciones.Text);
    dmgMain.qryExec.ExecSQL;

    ModalResult := mrOk;
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, 'Error al guardar el registro de proceso de lote');
  end;
end;

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

end.
