<?php

declare(strict_types=1);

namespace App\Modules\Developer;

/**
 * LogManagerService — Centralized Log Management & Activity Aggregation
 *
 * Provides log center capabilities for DX003:
 *   - Synchronization logs (from SyncLogService / storage/developer/sync_log.json)
 *   - Application logs (from storage/logs/*.log)
 *   - PHP error logs (from storage/logs/php_errors.log or php_error.log)
 *   - Activity feed (combined newest first)
 *   - Clear & Download capabilities
 *
 * Security:
 *   - File downloads restricted to storage/logs and storage/developer directories.
 *   - Path traversal prevented via realpath() verification.
 *
 * @package Inveetaire
 * @ref     DX003
 */
class LogManagerService
{
    private SyncLogService $syncLogService;
    private string $logsDir;
    private string $devDir;

    public function __construct()
    {
        $this->syncLogService = new SyncLogService();
        $this->logsDir        = ROOT_PATH . '/storage/logs';
        $this->devDir         = ROOT_PATH . '/storage/developer';

        if (!is_dir($this->logsDir)) {
            @mkdir($this->logsDir, 0755, true);
        }
    }

    /**
     * Retrieve aggregated logs by log category / type.
     *
     * @param string $type  'sync' | 'app' | 'php_error' | 'all'
     * @param int    $limit
     * @return array
     */
    public function getLogs(string $type = 'all', int $limit = 100): array
    {
        switch ($type) {
            case 'sync':
                return $this->syncLogService->getRecent($limit);

            case 'app':
                return $this->parseFileLog($this->getAppLogPath(), $limit, 'APP');

            case 'php_error':
                return $this->parseFileLog($this->getPhpErrorLogPath(), $limit, 'PHP');

            case 'all':
            default:
                $sync  = $this->syncLogService->getRecent($limit);
                $app   = $this->parseFileLog($this->getAppLogPath(), 50, 'APP');
                $php   = $this->parseFileLog($this->getPhpErrorLogPath(), 50, 'PHP');

                // Normalize for combined stream
                $stream = [];

                foreach ($sync as $s) {
                    $stream[] = [
                        'type'      => 'SYNC',
                        'timestamp' => $s['timestamp'] ?? date('Y-m-d H:i:s'),
                        'level'     => ($s['result'] ?? '') === 'error' ? 'ERROR' : (($s['is_dry_run'] ?? false) ? 'NOTICE' : 'INFO'),
                        'message'   => "Sync ({$s['direction']}): " . ($s['is_dry_run'] ? 'Dry Run' : 'Push') . " — " . ($s['result'] ?? 'unknown'),
                        'details'   => "Tables: {$s['tables_affected']}, Cols: {$s['columns_affected']}, Duration: {$s['duration_ms']}ms" . ($s['error_message'] ? " | Error: {$s['error_message']}" : ''),
                    ];
                }

                foreach ($app as $a) {
                    $stream[] = [
                        'type'      => 'APP',
                        'timestamp' => $a['timestamp'],
                        'level'     => $a['level'],
                        'message'   => $a['message'],
                        'details'   => $a['details'],
                    ];
                }

                foreach ($php as $p) {
                    $stream[] = [
                        'type'      => 'PHP',
                        'timestamp' => $p['timestamp'],
                        'level'     => 'ERROR',
                        'message'   => $p['message'],
                        'details'   => $p['details'],
                    ];
                }

                // Sort by timestamp descending
                usort($stream, static function (array $a, array $b): int {
                    return strcmp($b['timestamp'], $a['timestamp']);
                });

                return array_slice($stream, 0, $limit);
        }
    }

    /**
     * Get list of all log files in storage/logs directory with size and modification date.
     *
     * @return list<array{filename: string, path: string, size_kb: string, modified: string}>
     */
    public function getLogFilesList(): array
    {
        $result = [];

        // Add sync log file
        $syncFile = $this->devDir . '/sync_log.json';
        if (file_exists($syncFile)) {
            $result[] = [
                'key'      => 'sync',
                'filename' => 'sync_log.json',
                'category' => 'Synchronization Log',
                'size_kb'  => number_format(filesize($syncFile) / 1024, 1) . ' KB',
                'modified' => date('Y-m-d H:i:s', filemtime($syncFile)),
            ];
        }

        // Add .log files from storage/logs
        $files = glob($this->logsDir . '/*.log');
        if (is_array($files)) {
            foreach ($files as $file) {
                $basename = basename($file);
                $key = strpos($basename, 'error') !== false ? 'php_error' : 'app';

                $result[] = [
                    'key'      => $key,
                    'filename' => $basename,
                    'category' => strpos($basename, 'error') !== false ? 'PHP Error Log' : 'Application Log',
                    'size_kb'  => number_format(filesize($file) / 1024, 1) . ' KB',
                    'modified' => date('Y-m-d H:i:s', filemtime($file)),
                ];
            }
        }

        return $result;
    }

    /**
     * Clear log content by log type.
     *
     * @param string $type  'sync' | 'app' | 'php_error' | 'all'
     * @return bool
     */
    public function clearLog(string $type): bool
    {
        switch ($type) {
            case 'sync':
                $this->syncLogService->clear();
                return true;

            case 'app':
                return $this->truncateFile($this->getAppLogPath());

            case 'php_error':
                return $this->truncateFile($this->getPhpErrorLogPath());

            case 'all':
                $this->syncLogService->clear();
                $this->truncateFile($this->getAppLogPath());
                $this->truncateFile($this->getPhpErrorLogPath());
                return true;

            default:
                return false;
        }
    }

    /**
     * Resolve absolute file path for downloading a log file securely.
     *
     * @param string $type
     * @return string|null
     */
    public function getDownloadPath(string $type): ?string
    {
        switch ($type) {
            case 'sync':
                $path = $this->devDir . '/sync_log.json';
                return file_exists($path) ? $path : null;

            case 'app':
                $path = $this->getAppLogPath();
                return file_exists($path) ? $path : null;

            case 'php_error':
                $path = $this->getPhpErrorLogPath();
                return file_exists($path) ? $path : null;

            default:
                return null;
        }
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Private Helpers
    // ─────────────────────────────────────────────────────────────────────────

    private function getAppLogPath(): string
    {
        return $this->logsDir . '/app.log';
    }

    private function getPhpErrorLogPath(): string
    {
        $custom = $this->logsDir . '/php_errors.log';
        if (file_exists($custom)) {
            return $custom;
        }

        $alt = $this->logsDir . '/php_error.log';
        if (file_exists($alt)) {
            return $alt;
        }

        return $custom;
    }

    private function parseFileLog(string $filePath, int $limit, string $typeLabel): array
    {
        if (!file_exists($filePath) || filesize($filePath) === 0) {
            return [];
        }

        $lines = @file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if (!$lines) {
            return [];
        }

        $lines = array_reverse($lines); // newest first
        $parsed = [];

        foreach ($lines as $line) {
            if (count($parsed) >= $limit) {
                break;
            }

            // Example line: [2026-07-21 02:00:00] local.INFO: Something happened {"details": ...}
            if (preg_match('/^\[(.*?)\]\s+([\w\.\-]+):?\s*(.*)$/', $line, $m)) {
                $timestamp = $m[1];
                $levelRaw  = strtoupper($m[2]);
                $content   = $m[3];

                $level = 'INFO';
                if ((strpos($levelRaw, 'ERROR') !== false) || (strpos($levelRaw, 'CRITICAL') !== false)) {
                    $level = 'ERROR';
                } elseif (strpos($levelRaw, 'WARN') !== false) {
                    $level = 'WARNING';
                }

                $parsed[] = [
                    'type'      => $typeLabel,
                    'timestamp' => $timestamp,
                    'level'     => $level,
                    'message'   => $content,
                    'details'   => "Source: {$typeLabel} Log",
                ];
            } else {
                // Fallback for unformatted lines
                $parsed[] = [
                    'type'      => $typeLabel,
                    'timestamp' => date('Y-m-d H:i:s', filemtime($filePath)),
                    'level'     => 'INFO',
                    'message'   => $line,
                    'details'   => "Raw log line from " . basename($filePath),
                ];
            }
        }

        return $parsed;
    }

    private function truncateFile(string $path): bool
    {
        if (!file_exists($path)) {
            return true;
        }

        return @file_put_contents($path, '') !== false;
    }
}
