05-generation-rapports

<?php
class ReportGenerator {
    private array $data;
    
    public function __construct(array $rawData) {
        $this->data = $rawData;
    }
    
    public function generateSalesReport(string $format, string $period): string {
        $filtered = $this->filterByPeriod($period);
        
        $total = 0;
        $count = 0;
        $productStats = [];
        
        foreach($filtered as $sale) {
            $total += $sale['amount'];
            $count++;
            
            if(!isset($productStats[$sale['product']])) {
                $productStats[$sale['product']] = ['count' => 0, 'revenue' => 0];
            }
            $productStats[$sale['product']]['count']++;
            $productStats[$sale['product']]['revenue'] += $sale['amount'];
        }
        
        $average = $count > 0 ? $total / $count : 0;
        
        arsort($productStats);
        $topProducts = array_slice($productStats, 0, 5, true);
        
        if($format === 'pdf') {
            return $this->formatAsPDF($total, $average, $count, $topProducts);
        } elseif($format === 'excel') {
            return $this->formatAsExcel($total, $average, $count, $topProducts);
        } elseif($format === 'html') {
            return $this->formatAsHTML($total, $average, $count, $topProducts);
        } elseif($format === 'json') {
            return json_encode(['total' => $total, 'average' => $average, 
                               'count' => $count, 'top_products' => $topProducts]);
        }
        
        throw new Exception("Format non supporté");
    }
    
    private function filterByPeriod(string $period): array {
        $now = new DateTime();
        return array_filter($this->data, function($sale) use ($period, $now) {
            $saleDate = new DateTime($sale['date']);
            $diff = $now->diff($saleDate)->days;
            
            return match($period) {
                'week' => $diff <= 7,
                'month' => $diff <= 30,
                'quarter' => $diff <= 90,
                'year' => $diff <= 365,
                default => true
            };
        });
    }
    
    private function formatAsPDF($total, $avg, $count, $top): string {
        $pdf = new PDFLib();
        $pdf->addText("Total: $total EUR");
        $pdf->addText("Moyenne: $avg EUR");
        $pdf->addText("Nombre de ventes: $count");
        return $pdf->output();
    }
    
    private function formatAsExcel($total, $avg, $count, $top): string {
        $excel = new ExcelLib();
        $excel->setCellValue('A1', 'Total');
        $excel->setCellValue('B1', $total);
        return $excel->save();
    }
    
    private function formatAsHTML($total, $avg, $count, $top): string {
        $html = "<html><body>";
        $html .= "<h1>Rapport de ventes</h1>";
        $html .= "<p>Total: $total EUR</p>";
        $html .= "<p>Moyenne: $avg EUR</p>";
        $html .= "</body></html>";
        return $html;
    }
}

class PDFLib {
    public function addText(string $text): void { /* ... */ }
    public function output(): string { return "PDF content"; }
}

class ExcelLib {
    public function setCellValue(string $cell, $value): void { /* ... */ }
    public function save(): string { return "Excel content"; }
}

/*
=== USER STORIES ===

US1: En tant que manager, je veux générer des rapports au format CSV en plus 
     des formats existants.

US2: En tant que développeur, je veux tester la logique de calcul (total, moyenne, 
     top produits) indépendamment de la génération de fichiers.

US3: En tant que product owner, je veux ajouter un nouveau type de rapport 
     "Rapport de stock" avec des calculs différents mais les mêmes formats de sortie.

US4: En tant que manager, je veux pouvoir appliquer des filtres supplémentaires 
     (par catégorie de produit, par région) sans modifier la classe existante.

US5: En tant que développeur, je veux remplacer les bibliothèques PDF et Excel 
     par d'autres implémentations sans toucher à la logique métier.
*/