<?php
declare(strict_types=1);

function mal_stmt(mysqli $con,string $sql):mysqli_stmt{$s=$con->prepare($sql);if(!$s)throw new RuntimeException('Unable to prepare report query.');return $s;}
function mal_rows(mysqli $con,string $sql,string $types='',array $params=[]):array{$s=mal_stmt($con,$sql);if($types!=='')$s->bind_param($types,...$params);if(!$s->execute())throw new RuntimeException('Unable to execute report query.');$rows=[];$r=$s->get_result();while($x=$r->fetch_assoc())$rows[]=$x;$s->close();return $rows;}
function mal_table(mysqli $con,string $name):bool{$r=mal_rows($con,'SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?','s',[$name]);return(bool)$r;}
function mal_int($v):int{$v=filter_var($v,FILTER_VALIDATE_INT);return$v===false||$v<0?0:(int)$v;}
function mal_hhmm(int $m):string{return sprintf('%02d:%02d',intdiv(max(0,$m),60),max(0,$m)%60);}
function mal_time(string $v):string{$t=strtotime($v);return$t===false?$v:date('h:i A',$t);}
function mal_minutes(string $in,string $out,string $entry,string $exit=''):int{$a=strtotime($entry.' '.$in);$ed=$exit!==''&&$exit!=='0000-00-00'?$exit:$entry;$b=strtotime($ed.' '.$out);if($a===false||$b===false)return 0;if($b<$a)$b+=86400;return max(0,(int)round(($b-$a)/60));}
function mal_period_dates(string $from,string $to):array{$out=[];$d=new DateTimeImmutable($from);$last=new DateTimeImmutable($to);while($d<=$last){$out[]=$d->format('Y-m-d');$d=$d->modify('+1 day');}return$out;}
function mal_options(mysqli $con):array{
 $mode=(int)(mal_rows($con,'SELECT modes FROM jos_app_usersetting LIMIT 1')[0]['modes']??0);
 return[
  'mode'=>$mode,
  'months'=>mal_rows($con,'SELECT id,month name FROM jos_app_mmonth ORDER BY id'),
  'years'=>mal_rows($con,'SELECT id,year name FROM jos_app_year ORDER BY year DESC LIMIT 3'),
  'intervals'=>$mode===1&&mal_table($con,'jos_app_calender_customintervals')?mal_rows($con,'SELECT id,fromdate,todate,total_month_days FROM jos_app_calender_customintervals ORDER BY fromdate DESC'):[],
  'employees'=>mal_rows($con,'SELECT id,name,emp_code,status,payroll_location,branch_location,subbranch FROM jos_app_employee WHERE status=1 ORDER BY name,id')
 ];
}
function mal_filters(mysqli $con,array $src,array $opt):array{
 $currentMonth=(int)date('n');$currentYear=(int)date('Y');$yearId=0;foreach($opt['years'] as $y)if((int)$y['name']===$currentYear)$yearId=(int)$y['id'];
 $employee=mal_int($src['filter_name1']??0);
 return['interval'=>mal_int($src['filt_intervals']??0),'month'=>mal_int($src['filt_month']??$currentMonth),'year'=>mal_int($src['filt_year']??$yearId),'employee'=>$employee];
}
function mal_find(array $rows,int $id):?array{foreach($rows as $r)if((int)$r['id']===$id)return$r;return null;}
function mal_validate(array $f,array $o):array{
 $e=[];if(!mal_find($o['employees'],$f['employee']))$e[]='Select a valid authorized employee.';
 if($o['mode']===1){if(!mal_find($o['intervals'],$f['interval']))$e[]='Select a valid salary interval.';}
 else{if(!mal_find($o['months'],$f['month']))$e[]='Select a valid month.';if(!mal_find($o['years'],$f['year']))$e[]='Select a valid year.';}
 return$e;
}
function mal_period(array $f,array $o):array{
 if($o['mode']===1){$i=mal_find($o['intervals'],$f['interval']);return['from'=>$i['fromdate'],'to'=>$i['todate'],'label'=>date('d-m-Y',strtotime($i['fromdate'])).' - '.date('d-m-Y',strtotime($i['todate'])),'days'=>(int)$i['total_month_days']];}
 $m=mal_find($o['months'],$f['month']);$y=mal_find($o['years'],$f['year']);$year=(int)$y['name'];$month=(int)$f['month'];$from=sprintf('%04d-%02d-01',$year,$month);return['from'=>$from,'to'=>date('Y-m-t',strtotime($from)),'label'=>(string)$m['name'].' '.$year,'days'=>(int)date('t',strtotime($from))];
}
function mal_status(mysqli $con,int $emp,string $date,bool $hasLogs):string{
 if($hasLogs)return'Present';
 if(mal_table($con,'jos_app_leavestatus_log')){$r=mal_rows($con,'SELECT id FROM jos_app_leavestatus_log WHERE empid=? AND DATE(leavedate)=? AND status IN(2,3) ORDER BY id DESC LIMIT 1','is',[$emp,$date]);if($r)return'Leave';}
 if(mal_table($con,'jos_app_holiday')){$r=mal_rows($con,'SELECT id FROM jos_app_holiday WHERE DATE(date)=? LIMIT 1','s',[$date]);if($r)return'Holiday';}
 return strtolower(date('l',strtotime($date)))==='sunday'?'Weeklyoff':'Absent';
}
function mal_report(mysqli $con,array $f,array $o):array{
 $employee=mal_find($o['employees'],$f['employee']);$period=mal_period($f,$o);$dates=mal_period_dates($period['from'],$period['to']);
 $logs=mal_rows($con,'SELECT a.*,s.shift_type,s.shortname,s.in_time shift_in,s.out_time shift_out,s.workhrs,l.name location_name,l.gatepass FROM jos_app_attendance_log a LEFT JOIN jos_app_empshift s ON s.id=a.shiftid LEFT JOIN jos_app_orglocation l ON l.id=a.orglocation WHERE a.empid=? AND DATE(a.entrydate) BETWEEN ? AND ? ORDER BY a.entrydate,a.id','iss',[$f['employee'],$period['from'],$period['to']]);
 $by=[];foreach($logs as $x)$by[substr((string)$x['entrydate'],0,10)][]=$x;
 $rows=[];$summary=['Present'=>0,'Absent'=>0,'Weeklyoff'=>0,'Holiday'=>0,'Leave'=>0,'Working Minutes'=>0,'Gatepass Minutes'=>0,'Missing Logout'=>0];
 foreach($dates as $date){$day=$by[$date]??[];$normal=[];$gate=[];$worked=0;$gateM=0;$missing=false;$shift=null;
  foreach($day as $x){$shift=$shift??$x;$pair=[];$in=trim((string)($x['in_time']??''));$out=trim((string)($x['out_time']??''));if($in!=='')$pair[]=mal_time($in);if($out!=='')$pair[]=mal_time($out);elseif($in!=='')$missing=true;$m=mal_minutes($in,$out,$date,substr((string)($x['exitdate']??''),0,10));if((int)($x['gatepass']??1)===0){$gate[]=['location'=>$x['location_name'],'pair'=>$pair];$gateM+=$m;}else{$normal[]=['location'=>$x['location_name'],'pair'=>$pair];$worked+=$m;}}
  $status=mal_status($con,$f['employee'],$date,(bool)$normal);$summary[$status]=($summary[$status]??0)+1;$summary['Working Minutes']+=$worked;$summary['Gatepass Minutes']+=$gateM;if($missing)$summary['Missing Logout']++;
  $scheduled=0;if($shift){$si=strtotime($date.' '.$shift['shift_in']);$so=strtotime($date.' '.$shift['shift_out']);if($si!==false&&$so!==false){if($so<$si)$so+=86400;$scheduled=(int)(($so-$si)/60);}}
  $extra=max(0,$worked-$scheduled);$loss=$scheduled>0?max(0,$scheduled-$worked):0;
  $late=0;if($shift&&$normal){$first=$normal[0]['pair'][0]??'';$a=strtotime($date.' '.$first);$b=strtotime($date.' '.$shift['shift_in']);if($a!==false&&$b!==false)$late=max(0,(int)(($a-$b)/60));}
  $rows[]=['date'=>$date,'status'=>$missing?'Not logout':$status,'shift'=>$shift,'logs'=>$normal,'gate'=>$gate,'worked'=>$worked,'gate_minutes'=>$gateM,'extra'=>$extra,'loss'=>$loss,'late'=>$late];
 }
 $branch='';if((int)$employee['branch_location']>0)$branch=(string)(mal_rows($con,'SELECT name FROM jos_app_orglocation WHERE id=? LIMIT 1','i',[(int)$employee['branch_location']])[0]['name']??'');
 $payroll='';if((int)$employee['payroll_location']>0)$payroll=(string)(mal_rows($con,'SELECT name FROM jos_app_payrolllocation WHERE id=? LIMIT 1','i',[(int)$employee['payroll_location']])[0]['name']??'');
 $company=(string)(mal_rows($con,'SELECT store_name FROM jos_app_usersetting LIMIT 1')[0]['store_name']??APP_NAME);
 return compact('employee','period','rows','summary','branch','payroll','company');
}
