<?php
declare(strict_types=1);

function scm_stmt(mysqli $con,string $sql):mysqli_stmt{$s=$con->prepare($sql);if(!$s)throw new RuntimeException('Unable to prepare salary component operation.');return $s;}
function scm_rows(mysqli $con,string $sql,string $types='',array $params=[]):array{$s=scm_stmt($con,$sql);if($types)$s->bind_param($types,...$params);$s->execute();$rows=[];$r=$s->get_result();while($x=$r->fetch_assoc())$rows[]=$x;$s->close();return $rows;}
function scm_types(mysqli $con):array{return scm_rows($con,'SELECT id,componenttype name FROM jos_app_componenttype ORDER BY id');}
function scm_components(mysqli $con,int $exclude=0):array{$sql='SELECT id,componentname name FROM jos_app_salarycomponentmaster';$types='';$params=[];if($exclude){$sql.=' WHERE id<>?';$types='i';$params=[$exclude];}return scm_rows($con,$sql.' ORDER BY orderby,id',$types,$params);}
function scm_record(mysqli $con,int $id):?array{$r=scm_rows($con,'SELECT * FROM jos_app_salarycomponentmaster WHERE id=? LIMIT 1','i',[$id]);return $r[0]??null;}
function scm_in(array $rows,int $id):bool{foreach($rows as $r)if((int)$r['id']===$id)return true;return false;}
function scm_yesno():array{return [0=>'No',1=>'Yes'];}
function scm_defaults():array{return ['componenttype'=>0,'componentname'=>'','marathiname'=>'','shortname'=>'','functionname'=>'','calculation_on'=>[],'calculation_type'=>0,'status'=>1,'gross_status'=>0,'orderby'=>0,'editable'=>0,'dayshrsconfig'=>0,'otallow_status'=>0,'earning_status'=>0,'deduction_status'=>0,'deduction_remark'=>0,'attend_config'=>0,'roundup'=>0,'recurring'=>0];}
function scm_post():array{$v=scm_defaults();foreach(['componentname','marathiname','shortname','functionname'] as $k)$v[$k]=trim((string)($_POST[$k]??''));foreach(['componenttype','calculation_type','status','gross_status','orderby','editable','dayshrsconfig','otallow_status','earning_status','deduction_status','deduction_remark','attend_config','roundup','recurring'] as $k)$v[$k]=(int)($_POST[$k]??$v[$k]);$v['calculation_on']=array_values(array_unique(array_filter(array_map('intval',(array)($_POST['calculation_on']??[])),fn($x)=>$x>0)));return $v;}
function scm_validate(mysqli $con,array $v,int $id,array $types,array $components):array{$e=[];if(!scm_in($types,$v['componenttype']))$e[]='Select a valid component type.';if($v['componentname']==='')$e[]='Component Name is required.';if($v['marathiname']==='')$e[]='Component Name in Marathi is required.';if($v['shortname']==='')$e[]='Short Name is required.';if(mb_strlen($v['componentname'])>150)$e[]='Component Name is too long.';if(mb_strlen($v['marathiname'])>250)$e[]='Marathi Component Name is too long.';if(mb_strlen($v['shortname'])>50)$e[]='Short Name is too long.';if(!in_array($v['calculation_type'],[0,1,2],true))$e[]='Invalid calculation type.';foreach(['status','gross_status','editable','dayshrsconfig','otallow_status','earning_status','deduction_status','deduction_remark','attend_config','recurring'] as $k)if(!in_array($v[$k],[0,1],true))$e[]='Invalid '.str_replace('_',' ',$k).' value.';if($v['orderby']<0||$v['orderby']>9999)$e[]='Order By must be between 0 and 9999.';if($v['roundup']<0||$v['roundup']>5)$e[]='Invalid Round Up value.';foreach($v['calculation_on'] as $cid){if($cid===$id)$e[]='A component cannot calculate on itself.';elseif(!scm_in($components,$cid))$e[]='Invalid Calculation On component.';}
    foreach([['componentname',$v['componentname'],'Component Name'],['shortname',$v['shortname'],'Short Name']] as [$col,$value,$label]){$s=scm_stmt($con,"SELECT id FROM jos_app_salarycomponentmaster WHERE LOWER(TRIM(`$col`))=LOWER(TRIM(?)) AND id<>? LIMIT 1");$s->bind_param('si',$value,$id);$s->execute();if($s->get_result()->fetch_assoc())$e[]=$label.' already exists.';$s->close();}return array_values(array_unique($e));}
function scm_cycle(mysqli $con,int $id,array $selected):bool{if($id<1)return false;$graph=[];foreach(scm_rows($con,'SELECT id,calculation_on FROM jos_app_salarycomponentmaster') as $r)$graph[(int)$r['id']]=array_values(array_filter(array_map('intval',explode(',',(string)$r['calculation_on']))));$graph[$id]=$selected;$seen=[];$stack=[];$visit=function($n)use(&$visit,&$graph,&$seen,&$stack){if(isset($stack[$n]))return true;if(isset($seen[$n]))return false;$seen[$n]=1;$stack[$n]=1;foreach($graph[$n]??[] as $m)if($visit($m))return true;unset($stack[$n]);return false;};return $visit($id);}
function scm_save(mysqli $con,array $v,int $id):int{$csv=implode(',',$v['calculation_on']);$con->begin_transaction();try{if($id){$s=scm_stmt($con,'UPDATE jos_app_salarycomponentmaster SET componenttype=?,componentname=?,marathiname=?,shortname=?,calculation_type=?,gross_status=?,orderby=?,functionname=?,status=?,editable=?,earning_status=?,deduction_status=?,deduction_remark=?,otallow_status=?,attend_config=?,roundup=?,recurring=?,dayshrsconfig=?,calculation_on=?,date=CURDATE() WHERE id=?');$s->bind_param('isssiiisiiiiiiiiiisi',$v['componenttype'],$v['componentname'],$v['marathiname'],$v['shortname'],$v['calculation_type'],$v['gross_status'],$v['orderby'],$v['functionname'],$v['status'],$v['editable'],$v['earning_status'],$v['deduction_status'],$v['deduction_remark'],$v['otallow_status'],$v['attend_config'],$v['roundup'],$v['recurring'],$v['dayshrsconfig'],$csv,$id);}else{$s=scm_stmt($con,'INSERT INTO jos_app_salarycomponentmaster (componenttype,componentname,marathiname,shortname,calculation_type,gross_status,orderby,functionname,status,editable,earning_status,deduction_status,deduction_remark,otallow_status,attend_config,roundup,recurring,dayshrsconfig,calculation_on,date) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,CURDATE())');$s->bind_param('isssiiisiiiiiiiiiis',$v['componenttype'],$v['componentname'],$v['marathiname'],$v['shortname'],$v['calculation_type'],$v['gross_status'],$v['orderby'],$v['functionname'],$v['status'],$v['editable'],$v['earning_status'],$v['deduction_status'],$v['deduction_remark'],$v['otallow_status'],$v['attend_config'],$v['roundup'],$v['recurring'],$v['dayshrsconfig'],$csv);}$s->execute();if(!$id)$id=$s->insert_id;$s->close();$uid=(int)(current_user()['id']??0);$r=scm_record($con,$id);$s=scm_stmt($con,'INSERT INTO jos_app_salarycomponentmaster_editlog (salid,sysdate,componenttype,componentname,shortname,calculation_type,gross_status,orderby,functionname,status,editable,earning_status,deduction_status,calculation_on,otallow_status,attend_config,roundup,recurring,deduction_remark,modify_by) VALUES (?,NOW(),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');$s->bind_param('iissiiisiiiisiiiiii',$id,$r['componenttype'],$r['componentname'],$r['shortname'],$r['calculation_type'],$r['gross_status'],$r['orderby'],$r['functionname'],$r['status'],$r['editable'],$r['earning_status'],$r['deduction_status'],$r['calculation_on'],$r['otallow_status'],$r['attend_config'],$r['roundup'],$r['recurring'],$r['deduction_remark'],$uid);$s->execute();$s->close();$con->commit();return $id;}catch(Throwable $e){$con->rollback();throw $e;}}
function scm_calc_label(int $v):string{return [0=>'None',1=>'Plus',2=>'Minus'][$v]??'Unknown';}
function scm_round_label(int $v):string{return [0=>'None',1=>'Up',2=>'Down',3=>'Nearest',4=>'Mod',5=>'2 Decimals'][$v]??'Unknown';}
