<?php

/**
 * Admin audit logger.
 *
 * Writes JSON-lines to /var/log/xamxam.log (production) or
 * storage/logs/admin.log (dev / cli-server).
 *
 * Each entry: timestamp, actor (admin IP/UA), action, resource, status, context.
 *
 * DB mirroring: if the admin_audit_log table exists, every entry is also
 * inserted there — giving the admin panel instant queryable history without
 * depending on filesystem log parsing.
 */
class AdminLogger
{
    private string $logFile;
    private ?Database $db;

    public function __construct(?Database $db = null)
    {
        if (php_sapi_name() === 'cli-server') {
            $dir = defined('STORAGE_ROOT') ? STORAGE_ROOT . '/logs' : __DIR__ . '/../storage/logs';
            if (!is_dir($dir)) {
                mkdir($dir, 0755, true);
            }
            $this->logFile = $dir . '/admin.log';
        } else {
            $this->logFile = '/var/log/xamxam.log';
        }

        $this->db = $db;
    }

    // ── Convenience factory ───────────────────────────────────────────────────

    public static function make(): self
    {
        require_once APP_ROOT . '/src/Database.php';
        return new self(new Database());
    }

    // ── High-level log methods ────────────────────────────────────────────────

    /** TFE list: CSV export */
    public function logCsvExport(): void
    {
        $this->write('thesis', 'csv_export', 'success');
    }

    /** TFE list: CSV import */
    public function logCsvImport(int $imported, int $skipped): void
    {
        $this->write('thesis', 'csv_import', 'success', [
            'imported' => $imported,
            'skipped'  => $skipped,
        ]);
    }

    /** TFE: publish / unpublish (single or bulk) */
    public function logPublish(bool $published, array $thesisIds): void
    {
        $this->write('thesis', $published ? 'publish' : 'unpublish', 'success', [
            'count' => count($thesisIds),
            'ids'   => $thesisIds,
        ]);
    }

    /** TFE: visibility change */
    public function logVisibility(?int $accessTypeId, array $thesisIds): void
    {
        $this->write('thesis', 'set_visibility', 'success', [
            'access_type_id' => $accessTypeId,
            'count'          => count($thesisIds),
            'ids'            => $thesisIds,
        ]);
    }

    /** TFE: edit */
    public function logEdit(int $thesisId, string $title = ''): void
    {
        $this->write('thesis', 'edit', 'success', [
            'thesis_id' => $thesisId,
            'title'     => $title,
        ]);
    }

    /** TFE: add (new form submission from admin) */
    public function logAdd(int $thesisId, string $identifier, string $author): void
    {
        $this->write('thesis', 'add', 'success', [
            'thesis_id'  => $thesisId,
            'identifier' => $identifier,
            'author'     => $author,
        ]);
    }

    /** TFE: delete (single or bulk) */
    public function logDelete(array $thesisIds, bool $deleteAll = false): void
    {
        $this->write('thesis', 'delete', 'success', [
            'delete_all' => $deleteAll,
            'count'      => count($thesisIds),
            'ids'        => $thesisIds,
        ]);
    }

    /** Tags: rename, merge, delete */
    public function logTagAction(string $action, array $context = []): void
    {
        $this->write('tag', $action, 'success', $context);
    }

    /** Static pages / contenus */
    public function logPageEdit(string $slug): void
    {
        $this->write('page', 'edit', 'success', ['slug' => $slug]);
    }

    /** À propos content */
    public function logAproposEdit(string $key): void
    {
        $this->write('apropos', 'edit', 'success', ['key' => $key]);
    }

    /** Form structure (formulaire section in contenus) */
    public function logFormStructureEdit(string $section): void
    {
        $this->write('form_structure', 'edit', 'success', ['section' => $section]);
    }

    /** Accès étudiant·e links: create */
    public function logLinkCreate(string $slug, bool $hasPassword, ?string $expiresAt, ?string $objetRestriction): void
    {
        $this->write('share_link', 'create', 'success', [
            'slug'             => $slug,
            'has_password'     => $hasPassword,
            'expires_at'       => $expiresAt,
            'objet_restriction' => $objetRestriction,
        ]);
    }

    /** Accès étudiant·e links: toggle active/inactive */
    public function logLinkToggle(int $id, bool $nowActive): void
    {
        $this->write('share_link', $nowActive ? 'activate' : 'deactivate', 'success', [
            'link_id' => $id,
        ]);
    }

    /** Accès étudiant·e links: password change */
    public function logLinkPasswordChange(int $id, bool $removed): void
    {
        $this->write('share_link', 'set_password', 'success', [
            'link_id' => $id,
            'removed' => $removed,
        ]);
    }

    /** Accès étudiant·e links: archive (replaces delete) */
    public function logLinkArchive(int $id): void
    {
        $this->write('share_link', 'archive', 'success', ['link_id' => $id]);
    }

    /** File access requests: approve / reject */
    public function logAccessRequest(int $requestId, string $action, string $email, string $thesisTitle): void
    {
        $this->write('file_access_request', $action, 'success', [
            'request_id'  => $requestId,
            'email'       => $email,
            'thesis_title' => $thesisTitle,
        ]);
    }

    /** Parametres: maintenance toggle */
    public function logMaintenance(bool $enabled): void
    {
        $this->write('system', $enabled ? 'maintenance_on' : 'maintenance_off', 'success');
    }

    /** Parametres: DB export */
    public function logDbExport(): void
    {
        $this->write('system', 'db_export', 'success');
    }

    /** Files export (ZIP with all thesis files + manifest) */
    public function logFilesExport(int $fileCount, int $byteSize): void
    {
        $this->write('system', 'files_export', 'success', [
            'file_count' => $fileCount,
            'byte_size'  => $byteSize,
        ]);
    }

    /** Parametres: formulaire section toggles */
    public function logFormSettingsUpdate(array $newValues): void
    {
        $this->write('settings', 'formulaire_update', 'success', ['values' => $newValues]);
    }

    /** Parametres: objet types toggles */
    public function logObjetTypesUpdate(array $newValues): void
    {
        $this->write('settings', 'objet_types_update', 'success', ['values' => $newValues]);
    }

    /** Parametres: access restriction settings */
    public function logAccessRestrictionUpdate(array $newValues): void
    {
        $this->write('settings', 'access_restriction_update', 'success', ['values' => $newValues]);
    }

    /** Parametres: SMTP credentials update */
    public function logSmtpUpdate(bool $connectionOk): void
    {
        $this->write('settings', 'smtp_update', 'success', ['connection_ok' => $connectionOk]);
    }

    /** Parametres: PeerTube settings update */
    public function logPeerTubeUpdate(bool $enabled): void
    {
        $this->write('settings', 'peertube_update', 'success', ['enabled' => $enabled]);
    }

    /** Parametres: SMTP test */
    public function logSmtpTest(string $toEmail, bool $success, string $error = ''): void
    {
        $this->write('settings', 'smtp_test', $success ? 'success' : 'error', [
            'to'    => $toEmail,
            'error' => $error ?: null,
        ]);
    }

    /** Generic error entry (for catch blocks) */
    public function logError(string $resource, string $action, string $message, array $context = []): void
    {
        $this->write($resource, $action, 'error', array_merge($context, ['error' => $message]));
    }

    // ── Core write ────────────────────────────────────────────────────────────

    private function write(string $resource, string $action, string $status, array $context = []): void
    {
        $entry = [
            'timestamp' => date('c'),
            'ip'        => $_SERVER['REMOTE_ADDR'] ?? 'cli',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
            'resource'  => $resource,
            'action'    => $action,
            'status'    => $status,
        ];

        if (!empty($context)) {
            $entry['context'] = $context;
        }

        $line = json_encode($entry, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n";
        if (is_writable($this->logFile) || (!file_exists($this->logFile) && is_writable(dirname($this->logFile)))) {
            error_log($line, 3, $this->logFile);
        }

        if ($this->db !== null) {
            $this->insertDb($resource, $action, $status, $context);
        }
    }

    private function insertDb(string $resource, string $action, string $status, array $context): void
    {
        try {
            $pdo = $this->db->getConnection();
            $stmt = $pdo->prepare(
                'INSERT INTO admin_audit_log (ip, user_agent, resource, action, status, context)
                 VALUES (?, ?, ?, ?, ?, ?)'
            );
            $stmt->execute([
                $_SERVER['REMOTE_ADDR'] ?? 'cli',
                $_SERVER['HTTP_USER_AGENT'] ?? '',
                $resource,
                $action,
                $status,
                !empty($context) ? json_encode($context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : null,
            ]);
        } catch (\Throwable $e) {
            // DB logging is best-effort — never crash the app over it.
            error_log('[AdminLogger] DB insert failed: ' . $e->getMessage());
        }
    }
}
