<?php

declare(strict_types=1);

final class GrantsController
{
    private static function admin(): void
    {
        $action = ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' ? 'edit' : 'view';
        if (!PermissionService::can('grants', $action)) {
            http_response_code(403);
            echo h(t('grant.forbidden'));
            exit();
        }
    }
    private static function year(): int
    {
        return max(2000, min(2200, (int) ($_REQUEST['year'] ?? date('Y'))));
    }
    public static function index(): void
    {
        self::admin();
        $year = self::year();
        $applications = GrantService::applications($year);
        render('admin_grants.php', [
            'title' => t('grant.title'),
            'year' => $year,
            'annual' => GrantService::ensureYear($year),
            'stats' => GrantService::statistics($year),
            'details' => GrantService::detailedStatistics($year),
            'comparison' => GrantService::comparison($year),
            'applications' => $applications,
            'checklist' => GrantService::checklist($year),
            'documents' => GrantService::documents($year),
            'saved' => isset($_GET['saved']),
            'error' => (string) ($_GET['error'] ?? ''),
        ]);
    }
    public static function saveAnnual(): void
    {
        self::admin();
        $year = self::year();
        GrantService::ensureYear($year);
        $ints = ['members_count', 'volunteers_count', 'employees_count'];
        $nums = [
            'employees_fte',
            'treasury_amount',
            'actual_expenses',
            'actual_income',
            'forecast_expenses',
            'forecast_income',
            'voluntary_contributions',
        ];
        $texts = [
            'annual_report',
            'completed_actions',
            'planned_actions',
            'beneficiaries',
            'officers_notes',
            'in_kind_support',
        ];
        $values = [
            ':year' => $year,
            ':last_ag_date' => self::date($_POST['last_ag_date'] ?? null),
            ':uid' => Auth::id(),
        ];
        foreach ($ints as $f) {
            $values[':' . $f] = max(0, (int) ($_POST[$f] ?? 0));
        }
        foreach ($nums as $f) {
            $values[':' . $f] = max(0, self::amount($_POST[$f] ?? 0));
        }
        foreach ($texts as $f) {
            $values[':' . $f] = trim((string) ($_POST[$f] ?? ''));
        }
        $sets = [];
        foreach (array_merge($ints, $nums, $texts, ['last_ag_date']) as $f) {
            $sets[] = "$f=:$f";
        }
        $sql =
            'UPDATE grant_years SET ' .
            implode(',', $sets) .
            ',updated_by=:uid,updated_at=datetime(\'now\') WHERE year=:year';
        DB::pdo()->prepare($sql)->execute($values);
        AuditService::log('grant_year_saved', '/admin/grants/save', 'Bilan annuel ' . $year . ' mis à jour');
        header('Location: /admin/grants?year=' . $year . '&saved=1');
    }
    public static function saveApplication(): void
    {
        self::admin();
        $year = self::year();
        GrantService::ensureYear($year);
        $id = (int) ($_POST['application_id'] ?? 0);
        $existing = [];
        if ($id) {
            $q = DB::pdo()->prepare('SELECT * FROM grant_applications WHERE id=? AND year=?');
            $q->execute([$id, $year]);
            $existing = $q->fetch() ?: [];
        }
        $name = trim((string) ($_POST['organization_name'] ?? ($existing['organization_name'] ?? '')));
        if ($name === '') {
            header('Location: /admin/grants?year=' . $year . '&error=organisme');
            return;
        }
        $status = (string) ($_POST['status'] ?? ($existing['status'] ?? 'draft'));
        if (!isset(GrantService::STATUSES[$status])) {
            $status = 'draft';
        }
        $awardedRaw = $_POST['awarded_amount'] ?? ($existing['awarded_amount'] ?? '');
        $params = [
            ':year' => $year,
            ':name' => $name,
            ':deadline' => self::date($_POST['deadline'] ?? ($existing['deadline'] ?? null)),
            ':requested' => self::amount($_POST['requested_amount'] ?? ($existing['requested_amount'] ?? 0)),
            ':previous' => self::amount($_POST['previous_amount'] ?? ($existing['previous_amount'] ?? 0)),
            ':awarded' => trim((string) $awardedRaw) === '' ? null : self::amount($awardedRaw),
            ':status' => $status,
            ':notes' => trim((string) ($_POST['notes'] ?? ($existing['notes'] ?? ''))),
            ':uid' => Auth::id(),
        ];
        if ($id) {
            $params[':id'] = $id;
            DB::pdo()
                ->prepare(
                    'UPDATE grant_applications SET organization_name=:name,deadline=:deadline,requested_amount=:requested,previous_amount=:previous,awarded_amount=:awarded,status=:status,notes=:notes,updated_by=:uid,updated_at=datetime(\'now\') WHERE id=:id AND year=:year',
                )
                ->execute($params);
        } else {
            DB::pdo()
                ->prepare(
                    'INSERT INTO grant_applications(year,organization_name,deadline,requested_amount,previous_amount,awarded_amount,status,notes,created_by,updated_by) VALUES(:year,:name,:deadline,:requested,:previous,:awarded,:status,:notes,:uid,:uid)',
                )
                ->execute($params);
        }
        header('Location: /admin/grants?year=' . $year . '&saved=1');
    }
    public static function upload(): void
    {
        self::admin();
        $year = self::year();
        GrantService::ensureYear($year);
        $f = $_FILES['document'] ?? null;
        if (!$f || ($f['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK || ($f['size'] ?? 0) > 20_000_000) {
            header('Location: /admin/grants?year=' . $year . '&error=document');
            return;
        }
        $mime = (new finfo(FILEINFO_MIME_TYPE))->file($f['tmp_name']) ?: 'application/octet-stream';
        $allowed = ['application/pdf' => 'pdf', 'application/vnd.oasis.opendocument.text' => 'odt'];
        if (!isset($allowed[$mime])) {
            header('Location: /admin/grants?year=' . $year . '&error=format');
            return;
        }
        $dir = dirname(__DIR__, 2) . '/data/grants';
        if (!is_dir($dir)) {
            mkdir($dir, 0770, true);
        }
        $stored = bin2hex(random_bytes(18)) . '.' . $allowed[$mime];
        if (!move_uploaded_file($f['tmp_name'], $dir . '/' . $stored)) {
            throw new RuntimeException(t('error.document_storage'));
        }
        $category = (string) ($_POST['category'] ?? 'other');
        if (!isset(GrantService::CATEGORIES[$category])) {
            $category = 'other';
        }
        $global = isset($_POST['global_document']);
        $applicationId = (int) ($_POST['application_id'] ?? 0);
        if ($applicationId) {
            $check = DB::pdo()->prepare('SELECT 1 FROM grant_applications WHERE id=? AND year=?');
            $check->execute([$applicationId, $year]);
            if (!$check->fetchColumn()) {
                $applicationId = 0;
            }
        }
        if ($category === 'official_form' && !$applicationId) {
            @unlink($dir . '/' . $stored);
            header('Location: /admin/grants?year=' . $year . '&error=organisme');
            return;
        }
        DB::pdo()
            ->prepare(
                'INSERT INTO grant_documents(year,application_id,category,title,stored_name,original_name,mime_type,size_bytes,valid_until,notes,uploaded_by) VALUES(?,?,?,?,?,?,?,?,?,?,?)',
            )
            ->execute([
                $global ? null : $year,
                $applicationId ?: null,
                $category,
                trim((string) ($_POST['title'] ?? '')) ?: pathinfo((string) $f['name'], PATHINFO_FILENAME),
                $stored,
                basename((string) $f['name']),
                $mime,
                (int) $f['size'],
                self::date($_POST['valid_until'] ?? null),
                trim((string) ($_POST['notes'] ?? '')),
                Auth::id(),
            ]);
        header('Location: /admin/grants?year=' . $year . '&saved=1');
    }
    public static function download(): void
    {
        self::admin();
        $s = DB::pdo()->prepare('SELECT * FROM grant_documents WHERE id=?');
        $s->execute([(int) ($_GET['id'] ?? 0)]);
        $d = $s->fetch();
        $path = dirname(__DIR__, 2) . '/data/grants/' . ($d['stored_name'] ?? '');
        if (!$d || !is_file($path)) {
            http_response_code(404);
            echo h(t('grant.document_not_found'));
            return;
        }
        PrivacyService::logAccess('grant_document', (int) $d['id'], null, (string) $d['original_name']);
        header('Content-Type: ' . $d['mime_type']);
        header('Content-Disposition: inline; filename="' . rawurlencode($d['original_name']) . '"');
        header('Content-Length: ' . filesize($path));
        header('Cache-Control: private,no-store');
        readfile($path);
    }
    public static function print(): void
    {
        self::admin();
        $year = self::year();
        $annual = GrantService::ensureYear($year);
        $stats = GrantService::statistics($year);
        $details = GrantService::detailedStatistics($year);
        $comparison = GrantService::comparison($year);
        $applications = GrantService::applications($year);
        $documents = GrantService::documents($year);
        $generatedAt = new DateTimeImmutable('now', new DateTimeZone('Europe/Paris'));
        header('Content-Type: text/html; charset=UTF-8');
        require __DIR__ . '/../Views/admin_grants_print.php';
    }
    private static function date(mixed $v): ?string
    {
        $v = trim((string) $v);
        return preg_match('/^\d{4}-\d{2}-\d{2}$/', $v) ? $v : null;
    }
    private static function amount(mixed $v): float
    {
        return round((float) str_replace([' ', ' ', ','], ['', '', '.'], (string) $v), 2);
    }
}
