<?php
namespace App\Services;use Illuminate\Support\Collection;
final class LosersReportService{public const CRITICAL_VISIBILITY_LOSS=20.0;public function __construct(private VisibilityReportService $visibility,private WinnersReportService $comparisons){}public function rows(Collection$ids,\DateTimeInterface$from,\DateTimeInterface$to):Collection{return$this->comparisons->rows($ids,$from,$to)->map(function($row){$p=$row['previous'];$c=$row['current'];$out=!$row['first_measurement']&&$p!==null&&$c===null;$lost=$p!==null&&$c!==null&&$c>$p?$c-$p:null;return array_merge($row,['dropped_out'=>$out,'lost'=>$lost,'visibility_loss'=>max(0,round(((float)$this->visibility->weight($p)-(float)$this->visibility->weight($c))*100,2)),'category'=>$this->category($p,$c,$out,$row['first_measurement'])]);});}public function losers(Collection$r):Collection{return$r->filter(fn($x)=>$x['lost']!==null||$x['dropped_out'])->values();}public function category(?int$p,?int$c,bool$out=false,bool$first=false):string{if($first)return'first_measurement';if($out)return'dropped_out';if($p===null||$c===null||$c<=$p)return'not_loser';return match(true){$p<=3&&$c>3=>'dropped_top3',$p<=10&&$c>10=>'dropped_top10',$p<=20&&$c>20=>'dropped_top20',$p<=100&&$c>100=>'dropped_top100',default=>'declined'};}public function summary(Collection$r):array{$l=$this->losers($r);return['losers'=>$l->count(),'lost'=>$l->sum('lost'),'top3'=>$l->where('category','dropped_top3')->count(),'top10'=>$l->where('category','dropped_top10')->count(),'top100'=>$l->where('dropped_out',true)->count(),'visibility_loss'=>$l->isEmpty()?null:round($l->sum('visibility_loss')/max(1,$r->where('first_measurement',false)->count()),2)];}public function distribution(Collection$r):array{$l=$this->losers($r);return['1_3'=>$l->filter(fn($x)=>$x['lost']>=1&&$x['lost']<=3)->count(),'4_10'=>$l->filter(fn($x)=>$x['lost']>=4&&$x['lost']<=10)->count(),'11_20'=>$l->filter(fn($x)=>$x['lost']>=11&&$x['lost']<=20)->count(),'over20'=>$l->filter(fn($x)=>$x['lost']>20)->count(),'dropped_out'=>$l->where('dropped_out',true)->count()];}public function critical(Collection$r):Collection{return$this->losers($r)->filter(fn($x)=>in_array($x['category'],['dropped_out','dropped_top3','dropped_top10'],true)||$x['visibility_loss']>=self::CRITICAL_VISIBILITY_LOSS)->take(10)->values();}}
