<?php

declare(strict_types=1);

final class PrivacyService
{
    public static function logAccess(
        string $type,
        ?int $id,
        ?int $animalId,
        string $name,
        string $action = 'view',
    ): void {
        DB::pdo()
            ->prepare(
                'INSERT INTO private_file_access_log(user_id,resource_type,resource_id,animal_id,action,original_name,ip_address,user_agent) VALUES(?,?,?,?,?,?,?,?)',
            )
            ->execute([
                Auth::id(),
                $type,
                $id,
                $animalId,
                $action,
                mb_substr($name, 0, 240),
                $_SERVER['REMOTE_ADDR'] ?? null,
                mb_substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 500) ?: null,
            ]);
    }
    public static function accessLog(int $limit = 500): array
    {
        return DB::pdo()
            ->query(
                'SELECT l.*,COALESCE(u.display_name,u.username) actor,a.name animal_name FROM private_file_access_log l LEFT JOIN users u ON u.id=l.user_id LEFT JOIN animals a ON a.id=l.animal_id ORDER BY l.id DESC LIMIT ' .
                    max(1, min(2000, $limit)),
            )
            ->fetchAll(PDO::FETCH_ASSOC);
    }
    public static function retentionReview(int $years): array
    {
        $rows = DB::pdo()
            ->query(
                "SELECT dc.id,dc.name,dc.email,dc.phone,dc.updated_at,group_concat(DISTINCT r.role) roles,MAX(COALESCE(ad.adoption_date,ap.event_date,dc.updated_at)) last_activity,(SELECT COUNT(*) FROM animal_placements active JOIN animals a ON a.id=active.animal_id WHERE active.contact_id=dc.id AND active.event_type='foster' AND a.deleted_at IS NULL AND a.archived_at IS NULL AND lower(a.status)='fa') active_links FROM directory_contacts dc LEFT JOIN directory_contact_roles r ON r.contact_id=dc.id LEFT JOIN adoptions ad ON ad.adopter_contact_id=dc.id LEFT JOIN animal_placements ap ON ap.contact_id=dc.id WHERE dc.deleted_at IS NULL AND dc.kind='person' AND dc.anonymized_at IS NULL GROUP BY dc.id ORDER BY date(last_activity),dc.name COLLATE NOCASE",
            )
            ->fetchAll(PDO::FETCH_ASSOC);
        $out = [];
        foreach ($rows as $row) {
            $roles = array_filter(explode(',', (string) $row['roles']));
            $limits = [];
            foreach ($roles as $role) {
                $limits[] = match ($role) {
                    'adoptant' => AppSettings::int('privacy_retention_adopter_years', 1, 30),
                    'fa' => AppSettings::int('privacy_retention_foster_years', 1, 30),
                    'benevole' => AppSettings::int('privacy_retention_volunteer_years', 1, 30),
                    default => $years,
                };
            }
            $limit = $limits ? max($limits) : $years;
            $last = strtotime((string) $row['last_activity']) ?: time();
            if ($last > strtotime('-' . $limit . ' years')) {
                continue;
            }
            $row['policy_years'] = $limit;
            $row['blocked'] = (int) $row['active_links'] > 0;
            $out[] = $row;
        }
        return $out;
    }
    public static function exportContact(int $id): string
    {
        $db = DB::pdo();
        $s = $db->prepare(
            "SELECT dc.*,group_concat(r.role,',') roles,org.name organization_name FROM directory_contacts dc LEFT JOIN directory_contact_roles r ON r.contact_id=dc.id LEFT JOIN directory_contacts org ON org.id=dc.organization_id WHERE dc.id=? AND dc.deleted_at IS NULL GROUP BY dc.id",
        );
        $s->execute([$id]);
        $contact = $s->fetch(PDO::FETCH_ASSOC);
        if (!$contact) {
            throw new RuntimeException(t('privacy.contact_not_found'));
        }
        $queries = [
            'adoptions' =>
                'SELECT a.internal_code,a.name animal,ad.adoption_date,ad.notes FROM adoptions ad JOIN animals a ON a.id=ad.animal_id WHERE ad.adopter_contact_id=? ORDER BY ad.adoption_date',
            'placements' =>
                'SELECT a.internal_code,a.name animal,ap.event_type,ap.event_date,ap.reason,ap.notes FROM animal_placements ap JOIN animals a ON a.id=ap.animal_id WHERE ap.contact_id=? ORDER BY ap.event_date,ap.id',
            'deposited_animals' =>
                'SELECT internal_code,name,intake_date,intake_type,intake_reason,intake_circumstances FROM animals WHERE depositor_contact_id=? ORDER BY intake_date',
        ];
        $payload = ['generated_at' => (new DateTimeImmutable())->format(DATE_ATOM), 'contact' => $contact];
        foreach ($queries as $key => $sql) {
            $q = $db->prepare($sql);
            $q->execute([$id]);
            $payload[$key] = $q->fetchAll(PDO::FETCH_ASSOC);
        }
        $tmp = tempnam(sys_get_temp_dir(), 'globinours-contact-');
        if ($tmp === false) {
            throw new RuntimeException(t('privacy.export_failed'));
        }
        @unlink($tmp);
        $path = $tmp . '.zip';
        $zip = new ZipArchive();
        if ($zip->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            throw new RuntimeException(t('privacy.export_failed'));
        }
        $zip->addFromString(
            'contact.json',
            json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        );
        foreach (['adoptions', 'placements', 'deposited_animals'] as $key) {
            $zip->addFromString($key . '.csv', self::csv($payload[$key]));
        }
        $zip->addFromString(
            'README.txt',
            "Export de portabilité Globinours\nGénéré le " .
                date('d/m/Y H:i') .
                "\nDonnées à conserver dans un emplacement sécurisé.\n",
        );
        $zip->close();
        return $path;
    }
    public static function anonymize(int $id, string $reason): void
    {
        $reason = trim($reason);
        if ($reason === '') {
            throw new RuntimeException(t('privacy.reason_required'));
        }
        $db = DB::pdo();
        $s = $db->prepare(
            "SELECT * FROM directory_contacts WHERE id=? AND deleted_at IS NULL AND kind='person' AND anonymized_at IS NULL",
        );
        $s->execute([$id]);
        $contact = $s->fetch(PDO::FETCH_ASSOC);
        if (!$contact) {
            throw new RuntimeException(t('privacy.contact_not_found'));
        }
        $backup = BackupService::create('avant-anonymisation-contact-' . $id);
        $label = t('privacy.anonymous_contact') . ' #' . $id;
        $db->beginTransaction();
        try {
            $db->prepare(
                "UPDATE directory_contacts SET name=?,phone=NULL,email=NULL,address=NULL,postal_code=NULL,city=NULL,country='France',organization_id=NULL,notes=NULL,anonymized_at=datetime('now'),anonymization_reason=?,updated_at=datetime('now') WHERE id=?",
            )->execute([$label, $reason, $id]);
            $db->prepare(
                'UPDATE adoptions SET adopter_name=?,adopter_phone=NULL,adopter_email=NULL,adopter_address=NULL,adopter_postal_code=NULL,adopter_city=NULL WHERE adopter_contact_id=?',
            )->execute([$label, $id]);
            foreach ([(string) $contact['name']] as $old) {
                $db->prepare(
                    'UPDATE animal_movements SET contact_name=? WHERE lower(trim(contact_name))=lower(trim(?))',
                )->execute([$label, $old]);
            }
            $db->commit();
            AuditService::log(
                'contact_anonymized',
                '/settings/privacy/anonymize',
                'Contact anonymisé',
                'contact',
                $id,
                ['reason' => $reason, 'backup' => $backup['name'] ?? null],
            );
        } catch (Throwable $e) {
            if ($db->inTransaction()) {
                $db->rollBack();
            }
            throw $e;
        }
    }
    public static function maintenance(): array
    {
        $months = AppSettings::int('privacy_access_log_months', 1, 120);
        $s = DB::pdo()->prepare("DELETE FROM private_file_access_log WHERE datetime(accessed_at)<datetime('now',?)");
        $s->execute(['-' . $months . ' months']);
        return ['access_logs_removed' => $s->rowCount(), 'retention_months' => $months];
    }
    private static function csv(array $rows): string
    {
        if (!$rows) {
            return "\xEF\xBB\xBF";
        }
        $stream = fopen('php://temp', 'r+');
        fwrite($stream, "\xEF\xBB\xBF");
        fputcsv($stream, array_keys($rows[0]), ';');
        foreach ($rows as $row) {
            fputcsv($stream, array_values($row), ';');
        }
        rewind($stream);
        $out = stream_get_contents($stream);
        fclose($stream);
        return (string) $out;
    }
}
