unit frm_TrabajadorFichajeEditorModal;

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.ComCtrls, Data.DB, FireDAC.Comp.Client, FireDAC.Stan.Param, FireDAC.Stan.Intf,
  System.DateUtils, uAppTheme;

type
  TfrmTrabajadorFichajeEditorModal = class(TForm)
    pnlTop: TPanel;
    lblTitulo: TLabel;
    lblSubtitulo: TLabel;
    pnlBody: TPanel;
    lblFecha: TLabel;
    dtpFecha: TDateTimePicker;
    lblHoraInicio: TLabel;
    edtHoraInicio: TEdit;
    lblHoraFin: TLabel;
    edtHoraFin: TEdit;
    lblCalculoHoras: TLabel;
    lblCentro: TLabel;
    cmbCentro: TComboBox;
    lblTipoCierre: TLabel;
    cmbTipoCierre: TComboBox;
    lblObservaciones: TLabel;
    edtObservaciones: TEdit;
    chkValidado: TCheckBox;
    pnlBottom: TPanel;
    btnGuardar: TButton;
    btnCancelar: TButton;

    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure HoraChange(Sender: TObject);
    procedure btnGuardarClick(Sender: TObject);
  private
    FTrabajadorId: string;
    FFichajeId: Int64;
    procedure CargarCentros;
    procedure CargarFichajeExistente;
    function ParseTimeMinutes(const AStr: string; out AHour, AMin: Word): Boolean;
    procedure RecalcularDuracion;
  public
    class function Execute(const ATrabajadorId: string; AFecha: TDate; AFichajeId: Int64 = 0): Boolean;
  end;

var
  frmTrabajadorFichajeEditorModal: TfrmTrabajadorFichajeEditorModal;

implementation

uses
  dmg_Main, uDbErrorHandler;

{$R *.dfm}

class function TfrmTrabajadorFichajeEditorModal.Execute(const ATrabajadorId: string;
  AFecha: TDate; AFichajeId: Int64 = 0): Boolean;
var
  LFrm: TfrmTrabajadorFichajeEditorModal;
begin
  Result := False;
  LFrm := TfrmTrabajadorFichajeEditorModal.Create(Application);
  try
    LFrm.FTrabajadorId := ATrabajadorId;
    LFrm.FFichajeId := AFichajeId;
    LFrm.dtpFecha.Date := AFecha;
    if LFrm.ShowModal = mrOk then
      Result := True;
  finally
    LFrm.Free;
  end;
end;

procedure TfrmTrabajadorFichajeEditorModal.FormCreate(Sender: TObject);
begin
  FTrabajadorId := '';
  FFichajeId := 0;
  dtpFecha.Date := Date;
end;

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

  if FFichajeId > 0 then
  begin
    Caption := 'Modificar Marcaje de Fichaje';
    lblTitulo.Caption := 'Modificar Fichaje Existente (ID: ' + IntToStr(FFichajeId) + ')';
    CargarFichajeExistente;
  end
  else
  begin
    Caption := 'Nuevo Marcaje de Fichaje';
    lblTitulo.Caption := 'Nuevo Marcaje de Fichaje';
    if cmbCentro.ItemIndex < 0 then
      cmbCentro.ItemIndex := 0;
    if cmbTipoCierre.ItemIndex < 0 then
      cmbTipoCierre.ItemIndex := 0;
  end;

  RecalcularDuracion;
  edtHoraInicio.SetFocus;
end;

procedure TfrmTrabajadorFichajeEditorModal.CargarCentros;
var
  LQry: TFDQuery;
  LNom: string;
  LId: Integer;
begin
  cmbCentro.Items.Clear;
  LQry := TFDQuery.Create(nil);
  try
    LQry.Connection := dmgMain.dbConn;
    LQry.SQL.Text := 'SELECT id, nombre FROM ge_obradores WHERE activo = 1 ORDER BY nombre';
    LQry.Open;
    while not LQry.Eof do
    begin
      LId := LQry.FieldByName('id').AsInteger;
      LNom := Trim(LQry.FieldByName('nombre').AsString);
      if LNom <> '' then
        cmbCentro.Items.AddObject(LNom, TObject(LId));
      LQry.Next;
    end;
  finally
    LQry.Free;
  end;

  if cmbCentro.Items.Count = 0 then
    cmbCentro.Items.AddObject('OBRADOR PRINCIPAL', TObject(1));
end;

procedure TfrmTrabajadorFichajeEditorModal.CargarFichajeExistente;
var
  LQry: TFDQuery;
  LCentro: string;
  LIdx: Integer;
begin
  if FFichajeId <= 0 then Exit;

  LQry := TFDQuery.Create(nil);
  try
    LQry.Connection := dmgMain.dbConn;
    LQry.SQL.Text :=
      'SELECT fecha, TIME_FORMAT(hora_inicio, ''%H:%i'') AS hora_ini, ' +
      '       TIME_FORMAT(hora_fin, ''%H:%i'') AS hora_fin_fmt, ' +
      '       centro, tipo_cierre, validado, observaciones ' +
      'FROM ge_trabajadores_fichajes WHERE id = :id';
    LQry.ParamByName('id').AsLargeInt := FFichajeId;
    LQry.Open;

    if not LQry.IsEmpty then
    begin
      dtpFecha.Date := LQry.FieldByName('fecha').AsDateTime;
      edtHoraInicio.Text := LQry.FieldByName('hora_ini').AsString;
      edtHoraFin.Text := LQry.FieldByName('hora_fin_fmt').AsString;
      edtObservaciones.Text := LQry.FieldByName('observaciones').AsString;
      chkValidado.Checked := (LQry.FieldByName('validado').AsInteger = 1);

      LCentro := Trim(LQry.FieldByName('centro').AsString);
      LIdx := cmbCentro.Items.IndexOf(LCentro);
      if LIdx >= 0 then
        cmbCentro.ItemIndex := LIdx
      else if LCentro <> '' then
      begin
        cmbCentro.Items.AddObject(LCentro, TObject(1));
        cmbCentro.ItemIndex := cmbCentro.Items.Count - 1;
      end;

      LIdx := cmbTipoCierre.Items.IndexOf(LQry.FieldByName('tipo_cierre').AsString);
      if LIdx >= 0 then
        cmbTipoCierre.ItemIndex := LIdx;
    end;
  finally
    LQry.Free;
  end;
end;

function TfrmTrabajadorFichajeEditorModal.ParseTimeMinutes(const AStr: string; out AHour, AMin: Word): Boolean;
var
  LParts: TArray<string>;
  LH, LM: Integer;
  LClean: string;
begin
  Result := False;
  AHour := 0;
  AMin := 0;
  LClean := Trim(AStr);
  if LClean = '' then Exit;

  LParts := LClean.Split([':']);
  if Length(LParts) >= 2 then
  begin
    if TryStrToInt(Trim(LParts[0]), LH) and TryStrToInt(Trim(LParts[1]), LM) then
    begin
      if (LH >= 0) and (LH <= 23) and (LM >= 0) and (LM <= 59) then
      begin
        AHour := LH;
        AMin := LM;
        Result := True;
      end;
    end;
  end;
end;

procedure TfrmTrabajadorFichajeEditorModal.RecalcularDuracion;
var
  LHIni, LMIni, LHFin, LMFin: Word;
  LMinIni, LMinFin, LTotMin: Integer;
  LHorasDec: Double;
begin
  if ParseTimeMinutes(edtHoraInicio.Text, LHIni, LMIni) and
     ParseTimeMinutes(edtHoraFin.Text, LHFin, LMFin) then
  begin
    LMinIni := LHIni * 60 + LMIni;
    LMinFin := LHFin * 60 + LMFin;

    if LMinFin >= LMinIni then
      LTotMin := LMinFin - LMinIni
    else
      LTotMin := (24 * 60 - LMinIni) + LMinFin; // Cruce de medianoche

    LHorasDec := LTotMin / 60.0;
    lblCalculoHoras.Caption := Format('  Duración calculada: %d minutos (%s horas)',
      [LTotMin, FormatFloat('0.00', LHorasDec)]);
    lblCalculoHoras.Color := $00E1F5FE; // Azul muy suave
    lblCalculoHoras.Font.Color := $0001579B;
  end
  else
  begin
    lblCalculoHoras.Caption := '  Introduzca horas válidas en formato HH:mm (ej. 08:30 y 14:00)';
    lblCalculoHoras.Color := $00FFF3E0; // Ámbar suave
    lblCalculoHoras.Font.Color := $00E65100;
  end;
end;

procedure TfrmTrabajadorFichajeEditorModal.HoraChange(Sender: TObject);
begin
  RecalcularDuracion;
end;

procedure TfrmTrabajadorFichajeEditorModal.btnGuardarClick(Sender: TObject);
var
  LQry: TFDQuery;
  LHIni, LMIni, LHFin, LMFin: Word;
  LMinIni, LMinFin, LTotMin: Integer;
  LHorasDec: Double;
  LEmpresaId, LCentroId: Integer;
  LCentroNombre, LTipoCierre: string;
begin
  if not ParseTimeMinutes(edtHoraInicio.Text, LHIni, LMIni) then
  begin
    ShowMessage('Debe indicar una hora de inicio / entrada válida en formato HH:mm (ej. 08:00).');
    edtHoraInicio.SetFocus;
    Exit;
  end;

  if not ParseTimeMinutes(edtHoraFin.Text, LHFin, LMFin) then
  begin
    ShowMessage('Debe indicar una hora de fin / salida válida en formato HH:mm (ej. 14:00).');
    edtHoraFin.SetFocus;
    Exit;
  end;

  LMinIni := LHIni * 60 + LMIni;
  LMinFin := LHFin * 60 + LMFin;

  if LMinFin >= LMinIni then
    LTotMin := LMinFin - LMinIni
  else
    LTotMin := (24 * 60 - LMinIni) + LMinFin;

  LHorasDec := LTotMin / 60.0;

  LEmpresaId := StrToIntDef(dmgMain.CurrentCompanyId, 1);
  LCentroId := 1;
  LCentroNombre := 'OBRADOR';
  if cmbCentro.ItemIndex >= 0 then
  begin
    LCentroId := Integer(cmbCentro.Items.Objects[cmbCentro.ItemIndex]);
    LCentroNombre := cmbCentro.Text;
  end;

  LTipoCierre := 'MANUAL_SALIDA';
  if cmbTipoCierre.ItemIndex >= 0 then
    LTipoCierre := cmbTipoCierre.Text;

  LQry := TFDQuery.Create(nil);
  try
    LQry.Connection := dmgMain.dbConn;

    if FFichajeId > 0 then
    begin
      LQry.SQL.Text :=
        'UPDATE ge_trabajadores_fichajes SET ' +
        '  fecha = :fecha, ' +
        '  hora_inicio = :h_ini, ' +
        '  hora_fin = :h_fin, ' +
        '  minutos_totales = :min_tot, ' +
        '  horas_decimal = :h_dec, ' +
        '  id_centro = :id_cen, ' +
        '  centro = :centro, ' +
        '  tipo_cierre = :tipo_cierre, ' +
        '  validado = :val, ' +
        '  observaciones = :obs, ' +
        '  updated_at = CURRENT_TIMESTAMP ' +
        'WHERE id = :id';
      LQry.ParamByName('id').AsLargeInt := FFichajeId;
    end
    else
    begin
      LQry.SQL.Text :=
        'INSERT INTO ge_trabajadores_fichajes ( ' +
        '  id_empresa, fecha, id_trabajador, tipo_centro, id_centro, id_zona, ' +
        '  centro, hora_inicio, hora_fin, minutos_totales, horas_decimal, ' +
        '  coste_hora_aplicado, coste_total, tipo_cierre, validado, observaciones, created_at, updated_at ' +
        ') VALUES ( ' +
        '  :id_empresa, :fecha, :id_trab, ''OBRADOR'', :id_cen, :id_cen, ' +
        '  :centro, :h_ini, :h_fin, :min_tot, :h_dec, ' +
        '  0.00, 0.00, :tipo_cierre, :val, :obs, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ' +
        ')';
      LQry.ParamByName('id_empresa').AsInteger := LEmpresaId;
      LQry.ParamByName('id_trab').AsString := FTrabajadorId;
    end;

    LQry.ParamByName('fecha').AsDate := dtpFecha.Date;
    LQry.ParamByName('h_ini').AsString := Format('%.2d:%.2d:00', [LHIni, LMIni]);
    LQry.ParamByName('h_fin').AsString := Format('%.2d:%.2d:00', [LHFin, LMFin]);
    LQry.ParamByName('min_tot').AsInteger := LTotMin;
    LQry.ParamByName('h_dec').AsFloat := LHorasDec;
    LQry.ParamByName('id_cen').AsInteger := LCentroId;
    LQry.ParamByName('centro').AsString := LCentroNombre;
    LQry.ParamByName('tipo_cierre').AsString := LTipoCierre;
    if chkValidado.Checked then
      LQry.ParamByName('val').AsInteger := 1
    else
      LQry.ParamByName('val').AsInteger := 0;
    LQry.ParamByName('obs').AsString := Trim(edtObservaciones.Text);

    LQry.ExecSQL;
    ModalResult := mrOk;
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, 'Guardar Marcaje de Fichaje');
  end;
  LQry.Free;
end;

end.
