unit frm_MateriasPrimas;

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;

type
  TfrmMateriasPrimas = class(TfrmBaseList)
    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;
  private
    { Private declarations }
    procedure dbGridMainDrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
    procedure dbGridMainMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  public
    { Public declarations }
  end;

var
  frmMateriasPrimas: TfrmMateriasPrimas;

implementation

uses
  dmg_Main, frm_MateriasPrimasEditor, frm_ArticulosEditor;

{$R *.dfm}

procedure TfrmMateriasPrimas.FormShow(Sender: TObject);
begin
  AlphaFilterField := 'descripcion';
  StatusFilterField := 'activo';
  StatusValues.Add('Activo');
  StatusValues.Add('Inactivo');

  inherited;
  frmMateriasPrimas := Self;
  OnDestroy := FormDestroy;
  pnlSearch.Visible := True;
  lblSearch.Caption := 'Buscar Materia Prima:';
  
  // Asignar el manejador personalizado de dibujado de celdas
  dbGridMain.OnDrawColumnCell := dbGridMainDrawColumnCell;
  // Asignar manejador de MouseUp para toggle de activo al hacer clic en el checkbox
  dbGridMain.OnMouseUp := dbGridMainMouseUp;
end;

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

procedure TfrmMateriasPrimas.btnRefreshClick(Sender: TObject);
var
  LSql: string;
begin
  FQryMain.Close;
  LSql :=
    'SELECT t.id, t.descripcion, t.codigo_barras, t.activo, t.stock_minimo, t.stock_actual, ' +
    '       t.familia_desc, t.tipo_origen, t.es_articulo ' +
    'FROM (' +
    '  SELECT mp.id, ' +
    '         CONVERT(mp.descripcion USING utf8mb4) COLLATE utf8mb4_general_ci AS descripcion, ' +
    '         CONVERT(COALESCE(mp.codigo_barras, '''') USING utf8mb4) COLLATE utf8mb4_general_ci AS codigo_barras, ' +
    '         mp.activo, mp.stock_minimo, ' +
    '         (SELECT COALESCE(SUM(emp.stock_restante), 0.0000) ' +
    '          FROM ge_entradas_materias_primas emp ' +
    '          WHERE emp.id_materia_prima = mp.id) AS stock_actual, ' +
    '         CONVERT(COALESCE(f.descripcion, '''') USING utf8mb4) COLLATE utf8mb4_general_ci AS familia_desc, ' +
    '         CONVERT(''Materia Prima'' USING utf8mb4) COLLATE utf8mb4_general_ci AS tipo_origen, ' +
    '         0 AS es_articulo ' +
    '  FROM ge_materias_primas mp ' +
    '  LEFT JOIN ge_familias f ON mp.familia_id = f.id ' +
    '  UNION ALL ' +
    '  SELECT a.id, ' +
    '         CONVERT(a.descripcion USING utf8mb4) COLLATE utf8mb4_general_ci AS descripcion, ' +
    '         CONVERT(COALESCE(a.ean, '''') USING utf8mb4) COLLATE utf8mb4_general_ci AS codigo_barras, ' +
    '         a.activo, ' +
    '         CAST(0.00 AS DECIMAL(12,2)) AS stock_minimo, ' +
    '         CAST(0.0000 AS DECIMAL(12,4)) AS stock_actual, ' +
    '         CONVERT(COALESCE(f.descripcion, '''') USING utf8mb4) COLLATE utf8mb4_general_ci AS familia_desc, ' +
    '         CONVERT(''Artículo'' USING utf8mb4) COLLATE utf8mb4_general_ci AS tipo_origen, ' +
    '         1 AS es_articulo ' +
    '  FROM ge_articulos a ' +
    '  LEFT JOIN ge_familias f ON a.id_familia = f.id ' +
    '  WHERE a.ingrediente = 1 ' +
    ') t ';
    
  if Trim(edtSearch.Text) <> '' then
  begin
    LSql := LSql + 'WHERE t.descripcion LIKE :search OR t.codigo_barras LIKE :search ';
  end;
  LSql := LSql + 'ORDER BY t.descripcion';
  
  FQryMain.SQL.Text := LSql;
  
  if Trim(edtSearch.Text) <> '' then
    FQryMain.ParamByName('search').AsString := '%' + Trim(edtSearch.Text) + '%';
    
  FQryMain.Open;
  
  // Configurar títulos y anchos de columnas de la rejilla
  dbGridMain.Columns.Clear;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'id';
    Title.Caption := 'ID';
    Width := 60;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'descripcion';
    Title.Caption := 'Descripción';
    Width := 240;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'tipo_origen';
    Title.Caption := 'Tipo';
    Width := 95;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'familia_desc';
    Title.Caption := 'Familia';
    Width := 130;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'codigo_barras';
    Title.Caption := 'Cód. Barras';
    Width := 110;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'stock_minimo';
    Title.Caption := 'Stock Mín.';
    Width := 80;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'stock_actual';
    Title.Caption := 'Stock Act.';
    Width := 80;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'activo';
    Title.Caption := 'Activo';
    Width := 60;
  end;
end;

procedure TfrmMateriasPrimas.btnEditClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if FQryMain.FieldByName('es_articulo').AsInteger = 1 then
  begin
    if not Assigned(frmArticulosEditor) then
      Application.CreateForm(TfrmArticulosEditor, frmArticulosEditor);
      
    frmArticulosEditor.ParentForm := Self;
    frmArticulosEditor.ArticuloId := FQryMain.FieldByName('id').AsString;
    if frmArticulosEditor.ShowModal = mrOk then
      btnRefreshClick(nil);
  end
  else
  begin
    if not Assigned(frmMateriasPrimasEditor) then
      Application.CreateForm(TfrmMateriasPrimasEditor, frmMateriasPrimasEditor);
    
    frmMateriasPrimasEditor.ParentForm := Self;
    frmMateriasPrimasEditor.MateriaPrimaId := FQryMain.FieldByName('id').AsString;
    if frmMateriasPrimasEditor.ShowModal = mrOk then
      btnRefreshClick(nil);
  end;
end;

procedure TfrmMateriasPrimas.btnNewClick(Sender: TObject);
begin
  if not Assigned(frmMateriasPrimasEditor) then
    Application.CreateForm(TfrmMateriasPrimasEditor, frmMateriasPrimasEditor);
  
  frmMateriasPrimasEditor.ParentForm := Self;
  frmMateriasPrimasEditor.MateriaPrimaId := '';
  if frmMateriasPrimasEditor.ShowModal = mrOk then
    btnRefreshClick(nil);
end;

procedure TfrmMateriasPrimas.btnDeleteClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if FQryMain.FieldByName('es_articulo').AsInteger = 1 then
  begin
    if MessageDlg('Este registro es un artículo tipificado como ingrediente.' + sLineBreak +
                  '¿Desea desmarcarlo como ingrediente para que no aparezca en materias primas?',
                  mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      try
        dmgMain.qryExec.Close;
        dmgMain.qryExec.SQL.Text := 'UPDATE ge_articulos SET ingrediente = 0 WHERE id = :id';
        dmgMain.qryExec.ParamByName('id').AsString := FQryMain.FieldByName('id').AsString;
        dmgMain.qryExec.ExecSQL;
        btnRefreshClick(nil);
      except
        on E: Exception do
          ShowMessage('Error al desmarcar el artículo como ingrediente: ' + E.Message);
      end;
    end;
  end
  else
  begin
    if MessageDlg('¿Está seguro de que desea eliminar esta materia prima?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      try
        dmgMain.qryExec.Close;
        dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_materias_primas WHERE id = :id';
        dmgMain.qryExec.ParamByName('id').AsString := FQryMain.FieldByName('id').AsString;
        dmgMain.qryExec.ExecSQL;
        btnRefreshClick(nil);
      except
        on E: Exception do
          ShowMessage('Error al eliminar la materia prima: ' + E.Message);
      end;
    end;
  end;
end;

procedure TfrmMateriasPrimas.dbGridMainDrawColumnCell(Sender: TObject;
  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  LStockActual, LStockMinimo: Double;
  LBelowMin: Boolean;
  Checked: Boolean;
  DrawRect: TRect;
  DrawState: Cardinal;
  Grid: TDBGrid;
  IsOddRow: Boolean;
begin
  Grid := TDBGrid(Sender);
  LBelowMin := False;

  if Assigned(Grid.DataSource) and Assigned(Grid.DataSource.DataSet) and Grid.DataSource.DataSet.Active then
  begin
    LStockActual := Grid.DataSource.DataSet.FieldByName('stock_actual').AsFloat;
    if not Grid.DataSource.DataSet.FieldByName('stock_minimo').IsNull then
    begin
      LStockMinimo := Grid.DataSource.DataSet.FieldByName('stock_minimo').AsFloat;
      LBelowMin := LStockActual < LStockMinimo;
    end;
  end;

  // Si no está seleccionada, aplicamos color alterno o rojo pastel si está bajo mínimo
  if not (gdSelected in State) then
  begin
    if LBelowMin then
    begin
      // Color rojo pastel suave (BGR: $E0E0FF o similar)
      Grid.Canvas.Brush.Color := $D0D0FF;
    end
    else
    begin
      if Assigned(Grid.DataSource) and Assigned(Grid.DataSource.DataSet) and Grid.DataSource.DataSet.Active then
        IsOddRow := Odd(Grid.DataSource.DataSet.RecNo)
      else
        IsOddRow := False;

      if IsOddRow then
        Grid.Canvas.Brush.Color := TAppTheme.COLOR_GRID_ROW_ODD
      else
        Grid.Canvas.Brush.Color := TAppTheme.COLOR_GRID_ROW_EVEN;
    end;
  end;

  // Respetar dibujado de checkbox estándar si es columna boolean o activo
  if Assigned(Column.Field) and 
     ((Column.Field is TBooleanField) or 
      (Pos('activo', LowerCase(Column.Field.FieldName)) > 0) or 
      (Pos('active', LowerCase(Column.Field.FieldName)) > 0)) then
  begin
    Grid.Canvas.Font.Color := Grid.Canvas.Brush.Color;
    Grid.Canvas.FillRect(Rect);
    
    if not Column.ReadOnly then
      Column.ReadOnly := True;
      
    if (Column.Field is TBooleanField) and (TBooleanField(Column.Field).DisplayValues <> ';') then
      TBooleanField(Column.Field).DisplayValues := ';';
    
    if Column.Field is TBooleanField then
      Checked := Column.Field.AsBoolean
    else
    begin
      var LValInt: Integer;
      var LValStr := Trim(Column.Field.AsString);
      if TryStrToInt(LValStr, LValInt) then
        Checked := (LValInt <> 0)
      else
        Checked := SameText(LValStr, 'S') or 
                   SameText(LValStr, 'T') or 
                   SameText(LValStr, 'True') or
                   SameText(LValStr, '1');
    end;
                 
    DrawRect := Rect;
    DrawRect.Left := Rect.Left + (Rect.Width - 14) div 2;
    DrawRect.Right := DrawRect.Left + 14;
    DrawRect.Top := Rect.Top + (Rect.Height - 14) div 2;
    DrawRect.Bottom := DrawRect.Top + 14;
    
    DrawState := DFCS_BUTTONCHECK;
    if Checked then
      DrawState := DrawState or DFCS_CHECKED;
      
    DrawFrameControl(Grid.Canvas.Handle, DrawRect, DFC_BUTTON, DrawState);
  end
  else
  begin
    Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
  end;
end;

procedure TfrmMateriasPrimas.dbGridMainMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  LGridCoord: TGridCoord;
  LColumn: TColumn;
  LId: string;
  LCurrentActivo: Integer;
  LNewActivo: Integer;
  LRecNo: Integer;
begin
  if Button <> mbLeft then Exit;

  LGridCoord := dbGridMain.MouseCoord(X, Y);
  // Ignorar clic en la fila de títulos (Row=0) o fuera del grid
  if (LGridCoord.X < 0) or (LGridCoord.Y < 1) then Exit;

  // Ajustar por columna indicadora si está activa
  if dgIndicator in dbGridMain.Options then
  begin
    if LGridCoord.X = 0 then Exit; // clic en el indicador
    LColumn := dbGridMain.Columns[LGridCoord.X - 1];
  end
  else
    LColumn := dbGridMain.Columns[LGridCoord.X];

  // Solo actuar si se hizo clic en la columna 'activo'
  if not Assigned(LColumn.Field) then Exit;
  if not SameText(LColumn.Field.FieldName, 'activo') then Exit;
  if FQryMain.IsEmpty then Exit;

  LId := FQryMain.FieldByName('id').AsString;
  LRecNo := FQryMain.RecNo;

  // Determinar el valor actual
  if FQryMain.FieldByName('activo').IsNull then
    LCurrentActivo := 1
  else
    LCurrentActivo := FQryMain.FieldByName('activo').AsInteger;

  // Toggle: si es 1 pasa a 0, si es 0 pasa a 1
  if LCurrentActivo <> 0 then
    LNewActivo := 0
  else
    LNewActivo := 1;

  try
    dmgMain.qryExec.Close;
    if FQryMain.FieldByName('es_articulo').AsInteger = 1 then
      dmgMain.qryExec.SQL.Text := 'UPDATE ge_articulos SET activo = :activo WHERE id = :id'
    else
      dmgMain.qryExec.SQL.Text := 'UPDATE ge_materias_primas SET activo = :activo WHERE id = :id';
    dmgMain.qryExec.ParamByName('activo').AsInteger := LNewActivo;
    dmgMain.qryExec.ParamByName('id').AsString := LId;
    dmgMain.qryExec.ExecSQL;

    // Refrescar y restaurar posición por RecNo
    btnRefreshClick(nil);
    if (LRecNo > 0) and (LRecNo <= FQryMain.RecordCount) then
      FQryMain.RecNo := LRecNo;
  except
    on E: Exception do
      ShowMessage('Error al cambiar el estado: ' + E.Message);
  end;
end;

end.
