unit frm_CalidadHitosEditor;

interface

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

type
  TfrmCalidadHitosEditor = class(TfrmBase)
    lblCodigo: TLabel;
    edtCodigo: TEdit;
    lblNombre: TLabel;
    edtNombre: TEdit;
    lblModulo: TLabel;
    cboModulo: TComboBox;
    lblEvento: TLabel;
    cboEvento: TComboBox;
    lblDescripcion: TLabel;
    memDescripcion: TMemo;
    chkBloqueante: TCheckBox;
    chkActivo: TCheckBox;
    // ── Pestaña Documentos Asociados ─────────────────────────────────────────
    pnlDocsToolbar: TPanel;
    btnAsignarDoc: TSpeedButton;
    btnQuitarDoc: TSpeedButton;
    lblDocsNotice: TLabel;
    dbgDocs: TDBGrid;
    dsDocs: TDataSource;
    qryDocs: TFDQuery;

    procedure FormShow(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnSalirClick(Sender: TObject);
    procedure btnAsignarDocClick(Sender: TObject);
    procedure btnQuitarDocClick(Sender: TObject);
    procedure dbgDocsCellClick(Column: TColumn);
    procedure dbgDocsDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
  protected
    procedure DoAnadir; override;
    procedure DoModificar; override;
    procedure DoPrimero; override;
    procedure DoAnterior; override;
    procedure DoSiguiente; override;
    procedure DoUltimo; override;
  private
    FHitoId: Integer;
    FParentForm: TForm;
    procedure LoadData;
    procedure LoadAssociatedDocs;
  public
    property HitoId: Integer read FHitoId write FHitoId;
    property ParentForm: TForm read FParentForm write FParentForm;
  end;

var
  frmCalidadHitosEditor: TfrmCalidadHitosEditor;

implementation

uses
  dmg_Main, dmg_Calidad, uDbErrorHandler, frm_CalidadHitos, frm_SelectCalidadDocumento;

{$R *.dfm}

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

  // Vincular eventos de la barra superior heredada
  btnGuardar.OnClick := btnSaveClick;
  btnSalir.OnClick := btnSalirClick;

  // Ajustes de controles heredados
  pgcDetails.Visible := True;
  pnlSideToolbar.Visible := False;
  dbgItems.Visible := False;
  pnlFooter.Visible := False;

  if FHitoId > 0 then
    LoadData
  else
    DoAnadir;
end;

procedure TfrmCalidadHitosEditor.DoAnadir;
begin
  FHitoId := 0;
  edtCodigo.Text := '';
  edtNombre.Text := '';
  if cboModulo.Items.Count > 0 then cboModulo.ItemIndex := 0;
  if cboEvento.Items.Count > 0 then cboEvento.ItemIndex := 0;
  memDescripcion.Lines.Clear;
  chkBloqueante.Checked := True;
  chkActivo.Checked := True;
  qryDocs.Close;
  edtCodigo.SetFocus;
end;

procedure TfrmCalidadHitosEditor.DoModificar;
begin
  LoadData;
end;

procedure TfrmCalidadHitosEditor.DoPrimero;
begin
  // Navegación heredada opcional
end;

procedure TfrmCalidadHitosEditor.DoAnterior;
begin
end;

procedure TfrmCalidadHitosEditor.DoSiguiente;
begin
end;

procedure TfrmCalidadHitosEditor.DoUltimo;
begin
end;

function GetFieldBool(AField: TField): Boolean;
begin
  if (AField = nil) or AField.IsNull then
    Result := False
  else if AField.DataType = ftBoolean then
    Result := AField.AsBoolean
  else if AField.DataType in [ftSmallint, ftInteger, ftWord, ftShortint, ftLargeint] then
    Result := (AField.AsInteger <> 0)
  else
    Result := (AField.AsString = '1') or SameText(AField.AsString, 'True') or SameText(AField.AsString, 'S') or SameText(AField.AsString, 'T');
end;

procedure SetComboTextOrSelect(ACombo: TComboBox; const AText: string);
var
  LVal: string;
  LIdx, i: Integer;
begin
  LVal := Trim(AText);
  if LVal = '' then
  begin
    ACombo.ItemIndex := -1;
    ACombo.Text := '';
    Exit;
  end;

  LIdx := ACombo.Items.IndexOf(LVal);
  if LIdx < 0 then
  begin
    for i := 0 to ACombo.Items.Count - 1 do
    begin
      if SameText(ACombo.Items[i], LVal) then
      begin
        LIdx := i;
        Break;
      end;
    end;
  end;

  if LIdx >= 0 then
    ACombo.ItemIndex := LIdx
  else
  begin
    if ACombo.Style = csDropDownList then
    begin
      ACombo.Items.Add(LVal);
      ACombo.ItemIndex := ACombo.Items.Count - 1;
    end
    else
      ACombo.Text := LVal;
  end;
end;

procedure TfrmCalidadHitosEditor.LoadData;
var
  Qry: TFDQuery;
begin
  if FHitoId <= 0 then Exit;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT * FROM ge_calidad_hitos WHERE id = :id';
    Qry.ParamByName('id').AsInteger := FHitoId;
    Qry.Open;

    if not Qry.IsEmpty then
    begin
      edtCodigo.Text := Qry.FieldByName('codigo').AsString;
      edtNombre.Text := Qry.FieldByName('nombre').AsString;
      SetComboTextOrSelect(cboModulo, Qry.FieldByName('modulo_origen').AsString);
      SetComboTextOrSelect(cboEvento, Qry.FieldByName('evento_disparador').AsString);
      memDescripcion.Text := Qry.FieldByName('descripcion').AsString;
      chkBloqueante.Checked := GetFieldBool(Qry.FieldByName('es_bloqueante'));
      chkActivo.Checked := GetFieldBool(Qry.FieldByName('activo'));

      LoadAssociatedDocs;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmCalidadHitosEditor.LoadAssociatedDocs;
begin
  if FHitoId <= 0 then
  begin
    qryDocs.Close;
    Exit;
  end;

  qryDocs.Close;
  qryDocs.Connection := dmgMain.dbConn;
  qryDocs.SQL.Text :=
    'SELECT hd.id, hd.id_hito, hd.id_documento, hd.es_obligatorio, hd.orden, hd.activo, ' +
    '       d.codigo_doc, d.nombre_doc, d.periodicidad, d.departamento_responsable ' +
    'FROM ge_calidad_hitos_documentos hd ' +
    'JOIN ge_calidad_documentos d ON hd.id_documento = d.id ' +
    'WHERE hd.id_hito = :id ' +
    'ORDER BY hd.orden ASC, d.codigo_doc ASC';
  qryDocs.ParamByName('id').AsInteger := FHitoId;
  qryDocs.Open;

  // Configurar columnas de la rejilla
  dbgDocs.Columns.Clear;
  with dbgDocs.Columns.Add do
  begin
    FieldName := 'codigo_doc';
    Title.Caption := 'Código';
    Width := 110;
    ReadOnly := True;
  end;
  with dbgDocs.Columns.Add do
  begin
    FieldName := 'nombre_doc';
    Title.Caption := 'Nombre del Documento Normativo';
    Width := 280;
    ReadOnly := True;
  end;
  with dbgDocs.Columns.Add do
  begin
    FieldName := 'periodicidad';
    Title.Caption := 'Periodicidad';
    Width := 110;
    ReadOnly := True;
  end;
  with dbgDocs.Columns.Add do
  begin
    FieldName := 'departamento_responsable';
    Title.Caption := 'Departamento';
    Width := 100;
    ReadOnly := True;
  end;
  with dbgDocs.Columns.Add do
  begin
    FieldName := 'es_obligatorio';
    Title.Caption := 'Obligatorio';
    Width := 75;
    ReadOnly := True;
  end;
  with dbgDocs.Columns.Add do
  begin
    FieldName := 'orden';
    Title.Caption := 'Orden';
    Width := 50;
    ReadOnly := True;
  end;
end;

procedure TfrmCalidadHitosEditor.btnSaveClick(Sender: TObject);
var
  LCodigo, LNombre, LModulo, LEvento: string;
begin
  LCodigo := UpperCase(Trim(edtCodigo.Text));
  LNombre := Trim(edtNombre.Text);
  LModulo := Trim(cboModulo.Text);
  LEvento := Trim(cboEvento.Text);

  if LCodigo = '' then
  begin
    ShowMessage('Por favor, introduzca el código del hito.');
    edtCodigo.SetFocus;
    Exit;
  end;

  if LNombre = '' then
  begin
    ShowMessage('Por favor, introduzca el nombre del hito.');
    edtNombre.SetFocus;
    Exit;
  end;

  try
    dmgMain.qryExec.Close;
    if FHitoId <= 0 then
    begin
      dmgMain.qryExec.SQL.Text :=
        'INSERT INTO ge_calidad_hitos (codigo, nombre, modulo_origen, evento_disparador, descripcion, es_bloqueante, activo, created_at, id_user_creator) ' +
        'VALUES (:cod, :nom, :mod, :eve, :des, :bloq, :act, NOW(), :user)';
      dmgMain.qryExec.ParamByName('cod').AsString := LCodigo;
      dmgMain.qryExec.ParamByName('nom').AsString := LNombre;
      dmgMain.qryExec.ParamByName('mod').AsString := LModulo;
      dmgMain.qryExec.ParamByName('eve').AsString := LEvento;
      dmgMain.qryExec.ParamByName('des').AsString := Trim(memDescripcion.Text);
      dmgMain.qryExec.ParamByName('bloq').AsInteger := Ord(chkBloqueante.Checked);
      dmgMain.qryExec.ParamByName('act').AsInteger := Ord(chkActivo.Checked);
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
      dmgMain.qryExec.ExecSQL;

      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'SELECT LAST_INSERT_ID() AS new_id';
      dmgMain.qryExec.Open;
      FHitoId := dmgMain.qryExec.FieldByName('new_id').AsInteger;
    end
    else
    begin
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_calidad_hitos ' +
        'SET codigo = :cod, nombre = :nom, modulo_origen = :mod, evento_disparador = :eve, ' +
        '    descripcion = :des, es_bloqueante = :bloq, activo = :act, updated_at = NOW(), id_user_update = :user ' +
        'WHERE id = :id';
      dmgMain.qryExec.ParamByName('cod').AsString := LCodigo;
      dmgMain.qryExec.ParamByName('nom').AsString := LNombre;
      dmgMain.qryExec.ParamByName('mod').AsString := LModulo;
      dmgMain.qryExec.ParamByName('eve').AsString := LEvento;
      dmgMain.qryExec.ParamByName('des').AsString := Trim(memDescripcion.Text);
      dmgMain.qryExec.ParamByName('bloq').AsInteger := Ord(chkBloqueante.Checked);
      dmgMain.qryExec.ParamByName('act').AsInteger := Ord(chkActivo.Checked);
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
      dmgMain.qryExec.ParamByName('id').AsInteger := FHitoId;
      dmgMain.qryExec.ExecSQL;
    end;

    ModalResult := mrOk;
  except
    on E: Exception do
      ShowMessage('Error al guardar el hito: ' + E.Message);
  end;
end;

procedure TfrmCalidadHitosEditor.btnSalirClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

procedure TfrmCalidadHitosEditor.btnAsignarDocClick(Sender: TObject);
var
  LFrmSelect: TfrmSelectCalidadDocumento;
  I, LDocId, LCount, LNextOrder: Integer;
begin
  if FHitoId <= 0 then
  begin
    ShowMessage('Por favor, guarde primero los datos principales del hito antes de asociar documentos.');
    Exit;
  end;

  LFrmSelect := TfrmSelectCalidadDocumento.Create(Self);
  try
    LFrmSelect.ExcludeHitoId := FHitoId;
    LFrmSelect.MultiSelect := True;
    if LFrmSelect.ShowModal = mrOk then
    begin
      // Calcular siguiente orden disponible
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'SELECT COALESCE(MAX(orden), 0) AS max_orden FROM ge_calidad_hitos_documentos WHERE id_hito = :hito';
      dmgMain.qryExec.ParamByName('hito').AsInteger := FHitoId;
      dmgMain.qryExec.Open;
      LNextOrder := dmgMain.qryExec.FieldByName('max_orden').AsInteger + 1;

      LCount := 0;
      for I := 0 to LFrmSelect.SelectedIds.Count - 1 do
      begin
        LDocId := StrToIntDef(LFrmSelect.SelectedIds[I], 0);
        if LDocId > 0 then
        begin
          dmgMain.qryExec.Close;
          dmgMain.qryExec.SQL.Text :=
            'INSERT INTO ge_calidad_hitos_documentos (id_hito, id_documento, es_obligatorio, orden, activo, created_at, id_user_creator) ' +
            'VALUES (:hito, :doc, 1, :orden, 1, NOW(), :user)';
          dmgMain.qryExec.ParamByName('hito').AsInteger := FHitoId;
          dmgMain.qryExec.ParamByName('doc').AsInteger := LDocId;
          dmgMain.qryExec.ParamByName('orden').AsInteger := LNextOrder;
          dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
          dmgMain.qryExec.ExecSQL;
          Inc(LNextOrder);
          Inc(LCount);
        end;
      end;

      if LCount > 0 then
        LoadAssociatedDocs;
    end;
  finally
    LFrmSelect.Free;
  end;
end;

procedure TfrmCalidadHitosEditor.btnQuitarDocClick(Sender: TObject);
var
  LRelId: Integer;
begin
  if qryDocs.IsEmpty then Exit;

  if MessageDlg('¿Desea desvincular el documento de este hito?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    LRelId := qryDocs.FieldByName('id').AsInteger;
    dmgMain.qryExec.Close;
    dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_calidad_hitos_documentos WHERE id = :id';
    dmgMain.qryExec.ParamByName('id').AsInteger := LRelId;
    dmgMain.qryExec.ExecSQL;

    LoadAssociatedDocs;
  end;
end;

procedure TfrmCalidadHitosEditor.dbgDocsCellClick(Column: TColumn);
var
  LNewOblig: Integer;
begin
  if not Assigned(Column) or (Column.FieldName <> 'es_obligatorio') then Exit;
  if qryDocs.IsEmpty then Exit;

  if GetFieldBool(qryDocs.FieldByName('es_obligatorio')) then
    LNewOblig := 0
  else
    LNewOblig := 1;

  try
    dmgMain.qryExec.Close;
    dmgMain.qryExec.SQL.Text :=
      'UPDATE ge_calidad_hitos_documentos SET es_obligatorio = :ob, updated_at = NOW(), id_user_update = :user WHERE id = :id';
    dmgMain.qryExec.ParamByName('ob').AsInteger := LNewOblig;
    dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
    dmgMain.qryExec.ParamByName('id').AsInteger := qryDocs.FieldByName('id').AsInteger;
    dmgMain.qryExec.ExecSQL;

    LoadAssociatedDocs;
  except
    on E: Exception do
      ShowMessage('Error al actualizar obligatoriedad: ' + E.Message);
  end;
end;

procedure TfrmCalidadHitosEditor.dbgDocsDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  // El dibujado automático de checkboxes se gestiona a nivel base o personalizado
  dbgDocs.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

end.
