unit frm_DiaFichajesDetalleModal;

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.Grids, Vcl.DBGrids, Data.DB, FireDAC.Comp.Client, FireDAC.Stan.Param,
  FireDAC.Stan.Intf, System.DateUtils, System.UITypes, uAppTheme, frm_TrabajadorFichajeEditorModal;


type
  TfrmDiaFichajesDetalleModal = class(TForm)
    pnlTop: TPanel;
    lblTitulo: TLabel;
    lblTrabajadorInfo: TLabel;
    pnlResumenStatus: TPanel;
    lblHorasPactadas: TLabel;
    lblHorasTrabajadas: TLabel;
    lblBalance: TLabel;
    lblEstadoBadge: TLabel;
    lblAusenciaInfo: TLabel;
    pnlToolbar: TPanel;
    btnNuevoFichaje: TButton;
    btnModificarFichaje: TButton;
    btnEliminarFichaje: TButton;
    btnConmutarValidado: TButton;
    btnRefrescar: TButton;
    btnRegistrarFalta: TButton;
    btnEliminarFalta: TButton;
    dbgFichajes: TDBGrid;
    pnlBottom: TPanel;
    lblTotalesDia: TLabel;
    btnCerrar: TButton;
    qryFichajes: TFDQuery;
    dsFichajes: TDataSource;

    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnNuevoFichajeClick(Sender: TObject);
    procedure btnModificarFichajeClick(Sender: TObject);
    procedure btnEliminarFichajeClick(Sender: TObject);
    procedure btnConmutarValidadoClick(Sender: TObject);
    procedure btnRefrescarClick(Sender: TObject);
    procedure btnRegistrarFaltaClick(Sender: TObject);
    procedure btnEliminarFaltaClick(Sender: TObject);
    procedure dbgFichajesDblClick(Sender: TObject);

  private
    FTrabajadorId: string;
    FTrabajadorNombre: string;
    FFecha: TDate;
    FHorasPactadas: Double;
    FAusenciaTipo: string;
    FAusenciaObs: string;
    FModificado: Boolean;

    procedure ConfigurarColumnas;
    procedure CargarFichajes;
    procedure ActualizarResumenHoras(ATotalMinutos: Integer; ATotalHoras: Double);
  public
    class function Execute(const ATrabajadorId: string; const ATrabajadorNombre: string;
      AFecha: TDate; AHorasPactadas: Double; const AAusenciaTipo: string = '';
      const AAusenciaObs: string = ''): Boolean;
  end;

var
  frmDiaFichajesDetalleModal: TfrmDiaFichajesDetalleModal;

implementation

uses
  dmg_Main, uDbErrorHandler;

{$R *.dfm}

const
  NOMBRES_DIAS_SEMANA: array[1..7] of string = (
    'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'
  );

  NOMBRES_MESES: array[1..12] of string = (
    'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'
  );

class function TfrmDiaFichajesDetalleModal.Execute(const ATrabajadorId: string;
  const ATrabajadorNombre: string; AFecha: TDate; AHorasPactadas: Double;
  const AAusenciaTipo: string = ''; const AAusenciaObs: string = ''): Boolean;
var
  LFrm: TfrmDiaFichajesDetalleModal;
begin
  Result := False;
  LFrm := TfrmDiaFichajesDetalleModal.Create(Application);
  try
    LFrm.FTrabajadorId := ATrabajadorId;
    LFrm.FTrabajadorNombre := ATrabajadorNombre;
    LFrm.FFecha := AFecha;
    LFrm.FHorasPactadas := AHorasPactadas;
    LFrm.FAusenciaTipo := AAusenciaTipo;
    LFrm.FAusenciaObs := AAusenciaObs;
    LFrm.FModificado := False;

    LFrm.ShowModal;
    Result := LFrm.FModificado;
  finally
    LFrm.Free;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.FormCreate(Sender: TObject);
begin
  FFecha := Date;
  FHorasPactadas := 0.0;
  FModificado := False;
end;

procedure TfrmDiaFichajesDetalleModal.FormShow(Sender: TObject);
var
  LDow: Integer;
  LDiaStr, LMesStr: string;
  Y, M, D: Word;
begin
  TAppTheme.ApplyToForm(Self);

  DecodeDate(FFecha, Y, M, D);
  LDow := DayOfTheWeek(FFecha); // 1 = Lunes .. 7 = Domingo
  if (LDow >= 1) and (LDow <= 7) then
    LDiaStr := NOMBRES_DIAS_SEMANA[LDow]
  else
    LDiaStr := '';

  if (M >= 1) and (M <= 12) then
    LMesStr := NOMBRES_MESES[M]
  else
    LMesStr := '';

  lblTitulo.Caption := Format('Control de Fichajes - %s, %d de %s de %d', [LDiaStr, D, LMesStr, Y]);
  Caption := Format('Fichajes del día: %s (%.2d/%.2d/%d)', [LDiaStr, D, M, Y]);

  if FTrabajadorNombre <> '' then
    lblTrabajadorInfo.Caption := 'Trabajador: ' + FTrabajadorNombre
  else
    lblTrabajadorInfo.Caption := 'Trabajador ID: ' + FTrabajadorId;

  ConfigurarColumnas;
  CargarFichajes;
end;

procedure TfrmDiaFichajesDetalleModal.ConfigurarColumnas;
begin
  dbgFichajes.Columns.Clear;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'hora_ini_fmt';
    Title.Caption := 'Entrada';
    Width := 80;
    Alignment := taCenter;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'hora_fin_fmt';
    Title.Caption := 'Salida';
    Width := 80;
    Alignment := taCenter;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'minutos_totales';
    Title.Caption := 'Minutos';
    Width := 75;
    Alignment := taRightJustify;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'horas_decimal';
    Title.Caption := 'Horas';
    Width := 75;
    Alignment := taRightJustify;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'centro';
    Title.Caption := 'Centro / Obrador';
    Width := 180;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'tipo_cierre';
    Title.Caption := 'Tipo Cierre';
    Width := 130;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'validado_str';
    Title.Caption := 'Validado';
    Width := 85;
    Alignment := taCenter;
  end;

  with dbgFichajes.Columns.Add do
  begin
    FieldName := 'observaciones';
    Title.Caption := 'Observaciones';
    Width := 220;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.CargarFichajes;
var
  LTotMin: Integer;
  LTotHoras: Double;
begin
  qryFichajes.Close;
  try
    qryFichajes.SQL.Text :=
      'SELECT id, fecha, id_trabajador, id_empresa, ' +
      '       TIME_FORMAT(hora_inicio, ''%H:%i'') AS hora_ini_fmt, ' +
      '       TIME_FORMAT(hora_fin, ''%H:%i'') AS hora_fin_fmt, ' +
      '       minutos_totales, horas_decimal, centro, tipo_cierre, validado, ' +
      '       IF(validado = 1, ''SÍ'', ''NO'') AS validado_str, ' +
      '       observaciones ' +
      'FROM ge_trabajadores_fichajes ' +
      'WHERE id_trabajador = :id_trab AND fecha = :fecha ' +
      'ORDER BY hora_inicio ASC, id ASC';
    qryFichajes.ParamByName('id_trab').AsString := FTrabajadorId;
    qryFichajes.ParamByName('fecha').AsDate := FFecha;
    qryFichajes.Open;

    LTotMin := 0;
    LTotHoras := 0.0;
    while not qryFichajes.Eof do
    begin
      LTotMin := LTotMin + qryFichajes.FieldByName('minutos_totales').AsInteger;
      LTotHoras := LTotHoras + qryFichajes.FieldByName('horas_decimal').AsFloat;
      qryFichajes.Next;
    end;

    if not qryFichajes.IsEmpty then
      qryFichajes.First;

    lblTotalesDia.Caption := Format('Total acumulado: %d minutos (%s horas) en %d marcaje(s).',
      [LTotMin, FormatFloat('0.00', LTotHoras), qryFichajes.RecordCount]);

    // Recargar ausencia actual desde la base de datos si existe
    var LQryAus := TFDQuery.Create(nil);
    try
      LQryAus.Connection := dmgMain.dbConn;
      LQryAus.SQL.Text := 'SELECT tipo, observaciones FROM ge_trabajadores_ausencias WHERE id_trabajador = :id AND fecha_inicio <= :fec AND fecha_fin >= :fec LIMIT 1';
      LQryAus.ParamByName('id').AsString := FTrabajadorId;
      LQryAus.ParamByName('fec').AsDate := FFecha;
      LQryAus.Open;
      if not LQryAus.IsEmpty then
      begin
        FAusenciaTipo := LQryAus.FieldByName('tipo').AsString;
        FAusenciaObs := LQryAus.FieldByName('observaciones').AsString;
      end
      else if SameText(FAusenciaTipo, 'FALTA') or SameText(FAusenciaTipo, 'PERMISO') or SameText(FAusenciaTipo, 'AUSENCIA') then
      begin
        FAusenciaTipo := '';
        FAusenciaObs := '';
      end;
    finally
      LQryAus.Free;
    end;

    ActualizarResumenHoras(LTotMin, LTotHoras);
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, 'Cargar Fichajes del Día');
  end;
end;


procedure TfrmDiaFichajesDetalleModal.ActualizarResumenHoras(ATotalMinutos: Integer; ATotalHoras: Double);
var
  LDiff: Double;
  LSign: string;
begin
  lblHorasPactadas.Caption := Format('Horas pactadas: %s h', [FormatFloat('0.00', FHorasPactadas)]);
  lblHorasTrabajadas.Caption := Format('Horas trabajadas: %s h', [FormatFloat('0.00', ATotalHoras)]);

  LDiff := ATotalHoras - FHorasPactadas;
  if LDiff > 0 then
    LSign := '+'
  else
    LSign := '';

  lblBalance.Caption := Format('Balance: %s%s h', [LSign, FormatFloat('0.00', LDiff)]);

  if FAusenciaTipo <> '' then
  begin
    lblAusenciaInfo.Caption := 'ℹ️ ' + FAusenciaTipo;
    if FAusenciaObs <> '' then
      lblAusenciaInfo.Caption := lblAusenciaInfo.Caption + ' (' + FAusenciaObs + ')';
    lblAusenciaInfo.Visible := True;
  end
  else
  begin
    lblAusenciaInfo.Visible := False;
  end;

  // Lógica de colores del estado
  if FAusenciaTipo = 'VACACIONES' then
  begin
    lblEstadoBadge.Caption := 'VACACIONES';
    lblEstadoBadge.Color := $00E1BEE7; // Púrpura suave
    lblEstadoBadge.Font.Color := $004A148C;
  end
  else if (FAusenciaTipo = 'BAJA') or (FAusenciaTipo = 'BAJA_MEDICA') then
  begin
    lblEstadoBadge.Caption := 'BAJA MÉDICA';
    lblEstadoBadge.Color := $00D1C4E9;
    lblEstadoBadge.Font.Color := $00311B92;
  end
  else if FAusenciaTipo = 'FESTIVO' then
  begin
    lblEstadoBadge.Caption := 'FESTIVO';
    lblEstadoBadge.Color := $00FFE082; // Amarillo/Dorado
    lblEstadoBadge.Font.Color := $00FF6F00;
  end
  else if FAusenciaTipo <> '' then
  begin
    lblEstadoBadge.Caption := UpperCase(FAusenciaTipo);
    lblEstadoBadge.Color := $00E0E0E0;
    lblEstadoBadge.Font.Color := $00424242;
  end
  else if FHorasPactadas > 0 then
  begin
    if ATotalHoras = 0 then
    begin
      lblEstadoBadge.Caption := 'SIN FICHAJES (FALTA)';
      lblEstadoBadge.Color := $00F8D7DA; // Rojo alerta suave
      lblEstadoBadge.Font.Color := $00721C24;
    end
    else if Abs(LDiff) <= 0.05 then
    begin
      lblEstadoBadge.Caption := 'JORNADA COMPLETA';
      lblEstadoBadge.Color := $00D4EDDA; // Verde suave
      lblEstadoBadge.Font.Color := $00155724;
    end
    else if LDiff > 0.05 then
    begin
      lblEstadoBadge.Caption := 'SUPERÁVIT (HORAS EXTRA)';
      lblEstadoBadge.Color := $00CCE5FF; // Azul suave
      lblEstadoBadge.Font.Color := $00004085;
    end
    else
    begin
      lblEstadoBadge.Caption := 'DÉFICIT (JORNADA INCOMPLETA)';
      lblEstadoBadge.Color := $00FFF3CD; // Naranja/Ámbar suave
      lblEstadoBadge.Font.Color := $00856404;
    end;
  end
  else
  begin
    if ATotalHoras > 0 then
    begin
      lblEstadoBadge.Caption := 'EXTRA EN DÍA LIBRE';
      lblEstadoBadge.Color := $00D0EBFF;
      lblEstadoBadge.Font.Color := $001864AB;
    end
    else
    begin
      lblEstadoBadge.Caption := 'DESCANSO / NO LABORAL';
      lblEstadoBadge.Color := $00F1F3F5;
      lblEstadoBadge.Font.Color := $00495057;
    end;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.btnNuevoFichajeClick(Sender: TObject);
begin
  if TfrmTrabajadorFichajeEditorModal.Execute(FTrabajadorId, FFecha, 0) then
  begin
    FModificado := True;
    CargarFichajes;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.btnModificarFichajeClick(Sender: TObject);
var
  LId: Int64;
begin
  if qryFichajes.IsEmpty then
  begin
    ShowMessage('Seleccione primero el marcaje que desea modificar.');
    Exit;
  end;

  LId := qryFichajes.FieldByName('id').AsLargeInt;
  if TfrmTrabajadorFichajeEditorModal.Execute(FTrabajadorId, FFecha, LId) then
  begin
    FModificado := True;
    CargarFichajes;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.btnEliminarFichajeClick(Sender: TObject);
var
  LQry: TFDQuery;
  LId: Int64;
begin
  if qryFichajes.IsEmpty then Exit;

  LId := qryFichajes.FieldByName('id').AsLargeInt;
  if MessageDlg('¿Está seguro de eliminar este registro de fichaje?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    LQry := TFDQuery.Create(nil);
    try
      LQry.Connection := dmgMain.dbConn;
      LQry.SQL.Text := 'DELETE FROM ge_trabajadores_fichajes WHERE id = :id';
      LQry.ParamByName('id').AsLargeInt := LId;
      LQry.ExecSQL;
      FModificado := True;
      CargarFichajes;
    except
      on E: Exception do
        TDbErrorHandler.HandleException(E, 'Eliminar Marcaje de Fichaje');
    end;
    LQry.Free;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.btnConmutarValidadoClick(Sender: TObject);
var
  LQry: TFDQuery;
  LId: Int64;
  LNewVal: Integer;
begin
  if qryFichajes.IsEmpty then Exit;

  LId := qryFichajes.FieldByName('id').AsLargeInt;
  LNewVal := 1 - qryFichajes.FieldByName('validado').AsInteger;

  LQry := TFDQuery.Create(nil);
  try
    LQry.Connection := dmgMain.dbConn;
    LQry.SQL.Text := 'UPDATE ge_trabajadores_fichajes SET validado = :val, updated_at = CURRENT_TIMESTAMP WHERE id = :id';
    LQry.ParamByName('val').AsInteger := LNewVal;
    LQry.ParamByName('id').AsLargeInt := LId;
    LQry.ExecSQL;
    FModificado := True;
    CargarFichajes;
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, 'Conmutar Validación');
  end;
  LQry.Free;
end;

procedure TfrmDiaFichajesDetalleModal.btnRefrescarClick(Sender: TObject);
begin
  CargarFichajes;
end;

procedure TfrmDiaFichajesDetalleModal.dbgFichajesDblClick(Sender: TObject);
begin
  btnModificarFichajeClick(nil);
end;

procedure TfrmDiaFichajesDetalleModal.btnRegistrarFaltaClick(Sender: TObject);
var
  LMotivo: string;
  LQry: TFDQuery;
begin
  LMotivo := FAusenciaObs;
  if LMotivo = '' then
    LMotivo := 'Falta de asistencia';

  if InputQuery('Registrar Falta de Asistencia',
    'Trabajador: ' + FTrabajadorNombre + sLineBreak +
    'Fecha: ' + FormatDateTime('dddd, dd/mm/yyyy', FFecha) + sLineBreak + sLineBreak +
    'Introduzca el motivo de la falta o ausencia:', LMotivo) then
  begin
    if Trim(LMotivo) = '' then
      LMotivo := 'Falta de asistencia sin especificar';

    LQry := TFDQuery.Create(nil);
    try
      LQry.Connection := dmgMain.dbConn;
      LQry.SQL.Text := 'DELETE FROM ge_trabajadores_ausencias WHERE id_trabajador = :id AND fecha_inicio <= :fec AND fecha_fin >= :fec';
      LQry.ParamByName('id').AsString := FTrabajadorId;
      LQry.ParamByName('fec').AsDate := FFecha;
      LQry.ExecSQL;

      LQry.SQL.Text :=
        'INSERT INTO ge_trabajadores_ausencias ' +
        '(id_empresa, id_trabajador, fecha_inicio, fecha_fin, tipo, dias_laborables, estado, observaciones, created_at, id_user_creator) ' +
        'VALUES (:emp, :trab, :fec_ini, :fec_fin, :tipo, 1, ''CONFIRMADA'', :obs, NOW(), :user)';
      LQry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
      LQry.ParamByName('trab').AsString := FTrabajadorId;
      LQry.ParamByName('fec_ini').AsDate := FFecha;
      LQry.ParamByName('fec_fin').AsDate := FFecha;
      LQry.ParamByName('tipo').AsString := 'FALTA';
      LQry.ParamByName('obs').AsString := LMotivo;
      LQry.ParamByName('user').AsString := dmgMain.CurrentUserId;
      LQry.ExecSQL;


      FAusenciaTipo := 'FALTA';
      FAusenciaObs := LMotivo;
      FModificado := True;
      CargarFichajes;
      ShowMessage('Falta de asistencia registrada correctamente.');
    finally
      LQry.Free;
    end;
  end;
end;

procedure TfrmDiaFichajesDetalleModal.btnEliminarFaltaClick(Sender: TObject);
var
  LQry: TFDQuery;
begin
  if FAusenciaTipo = '' then
  begin
    ShowMessage('Este día no tiene ninguna falta o ausencia registrada.');
    Exit;
  end;

  if MessageDlg('¿Desea eliminar la falta o ausencia registrada para este día?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    LQry := TFDQuery.Create(nil);
    try
      LQry.Connection := dmgMain.dbConn;
      LQry.SQL.Text := 'DELETE FROM ge_trabajadores_ausencias WHERE id_trabajador = :id AND fecha_inicio <= :fec AND fecha_fin >= :fec';
      LQry.ParamByName('id').AsString := FTrabajadorId;
      LQry.ParamByName('fec').AsDate := FFecha;
      LQry.ExecSQL;

      FAusenciaTipo := '';
      FAusenciaObs := '';
      FModificado := True;
      CargarFichajes;
      ShowMessage('Falta o ausencia eliminada correctamente.');
    finally
      LQry.Free;
    end;
  end;
end;

end.

