unit frm_CalidadPlannerList;

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, Vcl.ComCtrls, frm_BaseList, uAppTheme, FireDAC.Comp.Client,
  FireDAC.Stan.Param, System.UITypes,
  dmg_Main, dmg_Calidad, Winapi.ShellAPI;

type
  TfrmCalidadPlannerList = class(TfrmBaseList)
    btnTomarRegistro: TSpeedButton;
    btnSincronizar: TSpeedButton;
    lblKpiCumplimiento: TLabel;
    lblFechaDesde: TLabel;
    lblFechaHasta: TLabel;
    lblDepto: TLabel;
    lblPeriodicidad: TLabel;
    lblEstado: TLabel;
    dtpDesde: TDateTimePicker;
    dtpHasta: TDateTimePicker;
    cboDepto: TComboBox;
    cboPeriodicidad: TComboBox;
    cboEstado: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject); override;
    procedure btnNewClick(Sender: TObject); override;
    procedure btnEditClick(Sender: TObject); override;
    procedure btnDeleteClick(Sender: TObject); override;
    procedure btnTomarRegistroClick(Sender: TObject);
    procedure btnSincronizarClick(Sender: TObject);
    procedure dtpDesdeChange(Sender: TObject);
    procedure dtpHastaChange(Sender: TObject);
    procedure cboFiltroChange(Sender: TObject);
    procedure dbGridMainDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
    procedure dbGridMainDblClick(Sender: TObject);
    procedure dbGridMainTitleClick(Column: TColumn);
  private
    { Private declarations }
    procedure CargarCombosFiltro;
    procedure AplicarFiltroPlanificador;
    procedure ConfigurarColumnasGrid;
    procedure CalcularKPIsCumplimiento;
  public
    { Public declarations }
  end;

var
  frmCalidadPlannerList: TfrmCalidadPlannerList;

implementation

{$R *.dfm}

uses frm_CalidadRegistroEditor, frm_CalidadDocumentoEditor;

procedure TfrmCalidadPlannerList.FormCreate(Sender: TObject);
begin
  inherited;
  if not Assigned(dmgCalidad) then
    Application.CreateForm(TdmgCalidad, dmgCalidad);
end;

procedure TfrmCalidadPlannerList.FormShow(Sender: TObject);
var
  Year, Month, Day: Word;
  LPrimerDiaMesAnterior: TDate;
begin
  inherited;
  pnlLeftFilter.Visible := True;

  DecodeDate(Date, Year, Month, Day);
  if Month = 1 then
    LPrimerDiaMesAnterior := EncodeDate(Year - 1, 12, 1)
  else
    LPrimerDiaMesAnterior := EncodeDate(Year, Month - 1, 1);

  dtpDesde.Date := LPrimerDiaMesAnterior;
  dtpHasta.Date := Date;

  CargarCombosFiltro;
  btnNuevo.Visible := True;
  btnEditar.Visible := True;
  btnBorrar.Visible := True;
  btnNuevo.Align := alLeft;
  btnEditar.Align := alLeft;
  btnBorrar.Align := alLeft;
  btnPrint.Align := alLeft;
  btnHelp.Align := alLeft;

  if Assigned(btnTomarRegistro) then btnTomarRegistro.Visible := False;
  if Assigned(btnSincronizar) then btnSincronizar.Visible := False;

  AplicarFiltroPlanificador;
end;

procedure TfrmCalidadPlannerList.CargarCombosFiltro;
var
  QG: TFDQuery;
begin
  // Combo Departamentos / Grupos reales de la BD
  cboDepto.Items.Clear;
  cboDepto.Items.Add('Todos los Departamentos');

  if Assigned(dmgMain) and Assigned(dmgMain.dbConn) then
  begin
    QG := TFDQuery.Create(nil);
    try
      QG.Connection := dmgMain.dbConn;
      QG.SQL.Text := 'SELECT nombre FROM ge_calidad_grupos WHERE (activo = 1 OR activo IS NULL) ORDER BY orden, nombre';
      QG.Open;
      while not QG.Eof do
      begin
        cboDepto.Items.Add(QG.FieldByName('nombre').AsString);
        QG.Next;
      end;
    finally
      QG.Free;
    end;
  end;

  if cboDepto.ItemIndex < 0 then
    cboDepto.ItemIndex := 0;

  // Combo Periodicidades
  cboPeriodicidad.Items.Clear;
  cboPeriodicidad.Items.Add('Todas las Periodicidades');
  cboPeriodicidad.Items.Add('DIARIO');
  cboPeriodicidad.Items.Add('TURNO');
  cboPeriodicidad.Items.Add('SEMANAL');
  cboPeriodicidad.Items.Add('MENSUAL');
  cboPeriodicidad.Items.Add('ANUAL');
  cboPeriodicidad.Items.Add('LOTE_FABRICACION');
  cboPeriodicidad.Items.Add('RECEPCION_MERCANCIA');
  if cboPeriodicidad.ItemIndex < 0 then
    cboPeriodicidad.ItemIndex := 0;

  // Combo Estado Semáforo: Por defecto PENDIENTES DE COMPLETAR (Índice 1)
  cboEstado.Items.Clear;
  cboEstado.Items.Add('Todos los Estados');
  cboEstado.Items.Add('📋 PENDIENTES DE COMPLETAR (Sin finalizar)');
  cboEstado.Items.Add('🟢 COMPLETADO');
  cboEstado.Items.Add('🟡 PENDIENTE EN PLAZO');
  cboEstado.Items.Add('🔴 VENCIDO / OMISIÓN');
  cboEstado.Items.Add('🟣 FALLO PCC / INCIDENCIA');
  if cboEstado.ItemIndex < 0 then
    cboEstado.ItemIndex := 1;
end;

procedure TfrmCalidadPlannerList.dbGridMainTitleClick(Column: TColumn);
var
  LFieldName: string;
begin
  if not Assigned(dsMain.DataSet) or not (dsMain.DataSet is TFDQuery) then Exit;

  LFieldName := Column.FieldName;
  if SameText(LFieldName, 'semaforo_color') then
    LFieldName := 'estado_agenda'
  else if SameText(LFieldName, 'departamento') then
    LFieldName := 'departamento'
  else if SameText(LFieldName, 'codigo_doc') then
    LFieldName := 'codigo_doc';

  try
    TFDQuery(dsMain.DataSet).IndexFieldNames := LFieldName;
  except
    // Si la columna es calculada o no indexable directamente
  end;
end;

procedure TfrmCalidadPlannerList.btnRefreshClick(Sender: TObject);
begin
  inherited;
  AplicarFiltroPlanificador;
end;

procedure TfrmCalidadPlannerList.dtpDesdeChange(Sender: TObject);
begin
  AplicarFiltroPlanificador;
end;

procedure TfrmCalidadPlannerList.dtpHastaChange(Sender: TObject);
begin
  AplicarFiltroPlanificador;
end;

procedure TfrmCalidadPlannerList.cboFiltroChange(Sender: TObject);
begin
  AplicarFiltroPlanificador;
end;

procedure TfrmCalidadPlannerList.AplicarFiltroPlanificador;
var
  LDeptoSel, LPerSel, LEstSel: string;
begin
  LDeptoSel := '';
  if cboDepto.ItemIndex > 0 then
    LDeptoSel := cboDepto.Text;

  LPerSel := '';
  if cboPeriodicidad.ItemIndex > 0 then
    LPerSel := cboPeriodicidad.Text;

  LEstSel := '';
  if cboEstado.ItemIndex > 0 then
  begin
    case cboEstado.ItemIndex of
      1: LEstSel := 'PENDIENTES_TODOS';
      2: LEstSel := 'VERDE';
      3: LEstSel := 'AMARILLO';
      4: LEstSel := 'ROJO';
      5: LEstSel := 'MORADO';
    end;
  end;

  dsMain.DataSet := dmgCalidad.CargarPlanificadorCumplimiento(dtpDesde.Date, dtpHasta.Date, LDeptoSel, LPerSel, LEstSel);
  ConfigurarColumnasGrid;
  CalcularKPIsCumplimiento;

  if Assigned(dsMain.DataSet) and dsMain.DataSet.Active and not dsMain.DataSet.IsEmpty then
    dsMain.DataSet.First;
end;

procedure TfrmCalidadPlannerList.ConfigurarColumnasGrid;
var
  i: Integer;
  Col: TColumn;
begin
  if not Assigned(dbGridMain.DataSource) or not Assigned(dbGridMain.DataSource.DataSet) then Exit;

  // Ajustar títulos y anchos de columnas
  for i := 0 to dbGridMain.Columns.Count - 1 do
  begin
    Col := dbGridMain.Columns[i];
    if SameText(Col.FieldName, 'semaforo_color') then
    begin
      Col.Title.Caption := 'Estado Cumplimiento';
      Col.Width := 180;
      Col.Alignment := taCenter;
    end;

    if SameText(Col.FieldName, 'codigo_doc') then
    begin
      Col.Title.Caption := 'Código';
      Col.Width := 110;
    end;

    if SameText(Col.FieldName, 'nombre_doc') then
    begin
      Col.Title.Caption := 'Formulario / Registro de Calidad';
      Col.Width := 380;
    end;

    if SameText(Col.FieldName, 'departamento') then
    begin
      Col.Title.Caption := 'Departamento';
      Col.Width := 220;
    end;

    if SameText(Col.FieldName, 'periodicidad') then
    begin
      Col.Title.Caption := 'Periodicidad';
      Col.Width := 110;
    end;

    if SameText(Col.FieldName, 'fecha_programada') then
    begin
      Col.Title.Caption := 'Fecha Plan';
      Col.Width := 90;
      Col.Alignment := taCenter;
    end;

    if SameText(Col.FieldName, 'turno') then
    begin
      Col.Title.Caption := 'Turno';
      Col.Width := 70;
      Col.Alignment := taCenter;
    end;

    if SameText(Col.FieldName, 'fecha_limite_toma') then
    begin
      Col.Title.Caption := 'Fecha Límite';
      Col.Width := 90;
      Col.Alignment := taCenter;
    end;

    if SameText(Col.FieldName, 'referencia_origen') then
    begin
      Col.Title.Caption := 'Origen / Proceso';
      Col.Width := 180;
      Col.Visible := True;
    end;

    if SameText(Col.FieldName, 'hito_nombre') then
    begin
      Col.Title.Caption := 'Hito Operativo';
      Col.Width := 180;
      Col.Visible := True;
    end;

    if SameText(Col.FieldName, 'id_agenda') or SameText(Col.FieldName, 'id_documento') or
       SameText(Col.FieldName, 'id_lote_fabricacion') or SameText(Col.FieldName, 'id_albaran_compra') or
       SameText(Col.FieldName, 'id_envio_recogida') or SameText(Col.FieldName, 'id_hito') or
       SameText(Col.FieldName, 'id_registro_cab') or SameText(Col.FieldName, 'grupo_nombre') or
       SameText(Col.FieldName, 'fecha_registro') or SameText(Col.FieldName, 'estado_evaluacion') or
       SameText(Col.FieldName, 'estado_agenda') then
    begin
      Col.Visible := False;
    end;
  end;
end;

procedure TfrmCalidadPlannerList.CalcularKPIsCumplimiento;
var
  LTotal, LCompletados, LPendientesEnPlazo, LVencidos, LNoConformes: Integer;
  LPorcentaje: Double;
  LColorKpi: TColor;
  LColorText: string;
begin
  if not Assigned(dsMain.DataSet) or not dsMain.DataSet.Active then Exit;

  LTotal := dsMain.DataSet.RecordCount;

  // Actualizar contador oficial de la ventana base
  if Assigned(lblRecordCount) then
    lblRecordCount.Caption := Format('Registros: %d', [LTotal]);

  if LTotal = 0 then
  begin
    lblKpiCumplimiento.Caption := 'Sin registros planificados para este rango';
    lblKpiCumplimiento.Font.Color := clGray;
    Exit;
  end;

  LCompletados := 0;
  LPendientesEnPlazo := 0;
  LVencidos := 0;
  LNoConformes := 0;

  dsMain.DataSet.DisableControls;
  try
    dsMain.DataSet.First;
    while not dsMain.DataSet.Eof do
    begin
      LColorText := UpperCase(dsMain.DataSet.FieldByName('semaforo_color').AsString);
      if SameText(LColorText, 'VERDE') then
        Inc(LCompletados)
      else if SameText(LColorText, 'AMARILLO') then
        Inc(LPendientesEnPlazo)
      else if SameText(LColorText, 'ROJO') then
        Inc(LVencidos)
      else if SameText(LColorText, 'MORADO') then
        Inc(LNoConformes);
      dsMain.DataSet.Next;
    end;
    dsMain.DataSet.First;
  finally
    dsMain.DataSet.EnableControls;
  end;

  LPorcentaje := (LCompletados / LTotal) * 100.0;
  if LPorcentaje >= 90.0 then
    LColorKpi := $00155724
  else if LPorcentaje >= 70.0 then
    LColorKpi := $00856404
  else
    LColorKpi := $00721C24;

  lblKpiCumplimiento.Caption := Format(
    'Cumplimiento: %.1f%%  |  🟢 %d completados  |  🟡 %d en plazo  |  🔴 %d vencidos  |  🟣 %d incidencias  (Total: %d)',
    [LPorcentaje, LCompletados, LPendientesEnPlazo, LVencidos, LNoConformes, LTotal]
  );
  lblKpiCumplimiento.Font.Color := LColorKpi;
end;

procedure TfrmCalidadPlannerList.dbGridMainDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  LColorText: string;
  LBgColor, LTextColor: TColor;
  LTextDisplay: string;
begin
  if SameText(Column.FieldName, 'semaforo_color') then
  begin
    LColorText := UpperCase(Column.Field.AsString);
    if SameText(LColorText, 'VERDE') then
    begin
      LBgColor := $00D4EDDA; // Verde claro
      LTextColor := $00155724; // Verde oscuro
      LTextDisplay := '🟢 COMPLETADO';
    end
    else if SameText(LColorText, 'MORADO') then
    begin
      LBgColor := $00E2D9F3; // Morado claro
      LTextColor := $0038146D; // Morado oscuro
      LTextDisplay := '🟣 FALLO PCC (INCIDENCIA)';
    end
    else if SameText(LColorText, 'ROJO') then
    begin
      LBgColor := $00F8D7DA; // Rojo claro
      LTextColor := $00721C24; // Rojo oscuro
      LTextDisplay := '🔴 VENCIDO (OMISIÓN)';
    end
    else
    begin
      LBgColor := $00FFF3CD; // Amarillo claro
      LTextColor := $00856404; // Amarillo oscuro
      LTextDisplay := '🟡 PENDIENTE EN PLAZO';
    end;

    dbGridMain.Canvas.Brush.Color := LBgColor;
    dbGridMain.Canvas.Font.Color := LTextColor;
    dbGridMain.Canvas.Font.Style := [fsBold];
    dbGridMain.Canvas.FillRect(Rect);
    dbGridMain.Canvas.TextRect(Rect, Rect.Left + 6, Rect.Top + 4, LTextDisplay);
  end
  else
  begin
    // Renderizado estándar por defecto de VCL
    dbGridMain.DefaultDrawColumnCell(Rect, DataCol, Column, State);
  end;
end;

procedure TfrmCalidadPlannerList.btnNewClick(Sender: TObject);
var
  LQryDocs: TFDQuery;
  LFrmSelectDoc: TForm;
  LCboDocs: TComboBox;
  LBtnOk, LBtnCancel: TButton;
  LDocId, LRegCabId: Integer;
  LForm: TfrmCalidadRegistroEditor;
begin
  // Cuadro de diálogo modal para seleccionar la plantilla/formulario de calidad
  LFrmSelectDoc := TForm.Create(Self);
  try
    LFrmSelectDoc.Caption := 'Seleccionar Formulario / Registro de Calidad';
    LFrmSelectDoc.Width := 480;
    LFrmSelectDoc.Height := 180;
    LFrmSelectDoc.Position := poOwnerFormCenter;
    LFrmSelectDoc.BorderStyle := bsDialog;

    var LLabel := TLabel.Create(LFrmSelectDoc);
    LLabel.Parent := LFrmSelectDoc;
    LLabel.Left := 20;
    LLabel.Top := 15;
    LLabel.Caption := 'Seleccione la plantilla de registro de calidad a cumplimentar:';

    LCboDocs := TComboBox.Create(LFrmSelectDoc);
    LCboDocs.Parent := LFrmSelectDoc;
    LCboDocs.Left := 20;
    LCboDocs.Top := 40;
    LCboDocs.Width := 420;
    LCboDocs.Style := csDropDownList;

    LQryDocs := TFDQuery.Create(nil);
    try
      LQryDocs.Connection := dmgMain.dbConn;
      LQryDocs.SQL.Text := 'SELECT id, codigo, nombre FROM ge_calidad_documentos WHERE (activo = 1 OR activo IS NULL) ORDER BY codigo, nombre';
      LQryDocs.Open;
      while not LQryDocs.Eof do
      begin
        LCboDocs.Items.AddObject(LQryDocs.FieldByName('codigo').AsString + ' - ' + LQryDocs.FieldByName('nombre').AsString, TObject(IntPtr(LQryDocs.FieldByName('id').AsInteger)));
        LQryDocs.Next;
      end;
    finally
      LQryDocs.Free;
    end;

    if LCboDocs.Items.Count = 0 then
    begin
      MessageDlg('No existen formularios o plantillas de calidad activas configuradas.', mtWarning, [mbOK], 0);
      Exit;
    end;
    LCboDocs.ItemIndex := 0;

    LBtnOk := TButton.Create(LFrmSelectDoc);
    LBtnOk.Parent := LFrmSelectDoc;
    LBtnOk.Caption := 'Aceptar';
    LBtnOk.ModalResult := mrOk;
    LBtnOk.Default := True;
    LBtnOk.Left := 240;
    LBtnOk.Top := 85;
    LBtnOk.Width := 90;

    LBtnCancel := TButton.Create(LFrmSelectDoc);
    LBtnCancel.Parent := LFrmSelectDoc;
    LBtnCancel.Caption := 'Cancelar';
    LBtnCancel.ModalResult := mrCancel;
    LBtnCancel.Cancel := True;
    LBtnCancel.Left := 350;
    LBtnCancel.Top := 85;
    LBtnCancel.Width := 90;

    if LFrmSelectDoc.ShowModal = mrOk then
    begin
      LDocId := Integer(IntPtr(LCboDocs.Items.Objects[LCboDocs.ItemIndex]));
      if LDocId > 0 then
      begin
        LRegCabId := dmgCalidad.GenerarRegistroDesdeDocumento(LDocId, Date, 0);
        if LRegCabId > 0 then
        begin
          LForm := TfrmCalidadRegistroEditor.Create(nil);
          try
            LForm.RegistroId := LRegCabId;
            if LForm.ShowModal = mrOk then
              AplicarFiltroPlanificador;
          finally
            LForm.Free;
          end;
        end;
      end;
    end;
  finally
    LFrmSelectDoc.Free;
  end;
end;

procedure TfrmCalidadPlannerList.btnEditClick(Sender: TObject);
var
  LDocId, LAgendaId, LRegCabId: Integer;
  LForm: TfrmCalidadRegistroEditor;
begin
  if not Assigned(dsMain.DataSet) or dsMain.DataSet.IsEmpty then
  begin
    MessageDlg('Seleccione un registro planificado en la lista para editar.', mtInformation, [mbOK], 0);
    Exit;
  end;

  LDocId := dsMain.DataSet.FieldByName('id_documento').AsInteger;
  LAgendaId := dsMain.DataSet.FieldByName('id_agenda').AsInteger;
  LRegCabId := dsMain.DataSet.FieldByName('id_registro_cab').AsInteger;

  // 1. Buscar si ya existe un registro creado para esta tarea de agenda
  if (LRegCabId <= 0) and (LAgendaId > 0) then
  begin
    dmgCalidad.qryExecCalidad.Close;
    dmgCalidad.qryExecCalidad.SQL.Text := 'SELECT id FROM ge_calidad_registros_cab WHERE id_calidad_agenda = :aid LIMIT 1';
    dmgCalidad.qryExecCalidad.ParamByName('aid').AsInteger := LAgendaId;
    dmgCalidad.qryExecCalidad.Open;
    if not dmgCalidad.qryExecCalidad.IsEmpty then
      LRegCabId := dmgCalidad.qryExecCalidad.Fields[0].AsInteger;
  end;

  // 2. Abrir o generar el registro de cabecera
  if LRegCabId <= 0 then
    LRegCabId := dmgCalidad.GenerarRegistroDesdeDocumento(LDocId, Trunc(dsMain.DataSet.FieldByName('fecha_programada').AsDateTime), LAgendaId);

  if LRegCabId > 0 then
  begin
    LForm := TfrmCalidadRegistroEditor.Create(nil);
    try
      LForm.RegistroId := LRegCabId;
      if LForm.ShowModal = mrOk then
      begin
        if LAgendaId > 0 then
          dmgCalidad.VincularRegistroAAgenda(LAgendaId, LRegCabId);
        AplicarFiltroPlanificador;
      end;
    finally
      LForm.Free;
    end;
  end;
end;

procedure TfrmCalidadPlannerList.btnDeleteClick(Sender: TObject);
var
  LAgendaId, LRegCabId: Integer;
begin
  if not Assigned(dsMain.DataSet) or dsMain.DataSet.IsEmpty then
  begin
    MessageDlg('Seleccione un registro en la lista para eliminar.', mtInformation, [mbOK], 0);
    Exit;
  end;

  if MessageDlg('¿Está seguro de que desea eliminar la tarea/registro de calidad seleccionado?',
                 mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
    Exit;

  LAgendaId := dsMain.DataSet.FieldByName('id_agenda').AsInteger;
  LRegCabId := dsMain.DataSet.FieldByName('id_registro_cab').AsInteger;

  try
    dmgMain.qryExec.Close;
    if LRegCabId > 0 then
    begin
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_calidad_registros_det WHERE id_registro_cab = :id';
      dmgMain.qryExec.ParamByName('id').AsInteger := LRegCabId;
      dmgMain.qryExec.ExecSQL;

      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_calidad_registros_cab WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsInteger := LRegCabId;
      dmgMain.qryExec.ExecSQL;
    end;

    if LAgendaId > 0 then
    begin
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_calidad_agenda WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsInteger := LAgendaId;
      dmgMain.qryExec.ExecSQL;
    end;

    AplicarFiltroPlanificador;
    MessageDlg('Registro de calidad eliminado correctamente.', mtInformation, [mbOK], 0);
  except
    on E: Exception do
      MessageDlg('Error al eliminar el registro: ' + E.Message, mtError, [mbOK], 0);
  end;
end;

procedure TfrmCalidadPlannerList.btnTomarRegistroClick(Sender: TObject);
begin
  btnEditClick(Sender);
end;

procedure TfrmCalidadPlannerList.btnSincronizarClick(Sender: TObject);
begin
  dmgCalidad.SincronizarAgendaAutomatica(dtpHasta.Date);
  AplicarFiltroPlanificador;
end;

procedure TfrmCalidadPlannerList.dbGridMainDblClick(Sender: TObject);
begin
  btnEditClick(Sender);
end;

end.
