<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Responses\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;

class LogController extends Controller
{
    protected string $logPath;

    public function __construct()
    {
        $this->logPath = storage_path('logs');
    }

    public function index(Request $request)
    {
        try {
            $files = [];
            if (File::isDirectory($this->logPath)) {
                $logFiles = File::files($this->logPath);
                foreach ($logFiles as $file) {
                    $filename = $file->getFilename();
                    if (pathinfo($filename, PATHINFO_EXTENSION) === 'log') {
                        $files[] = [
                            'name' => $filename,
                            'size' => $this->formatFileSize($file->getSize()),
                            'size_bytes' => $file->getSize(),
                            'modified' => date('Y-m-d H:i:s', $file->getMTime()),
                        ];
                    }
                }
            }

            usort($files, function ($a, $b) {
                return strtotime($b['modified']) - strtotime($a['modified']);
            });

            return response()->json(ApiResponse::success($files));
        } catch (\Exception $e) {
            return response()->json(ApiResponse::error(50001, '获取日志文件列表失败：'.$e->getMessage()));
        }
    }

    public function read(Request $request)
    {
        try {
            $request->validate([
                'filename' => 'required|string',
                'page' => 'nullable|integer|min:1',
                'page_size' => 'nullable|integer|min:1|max:500',
                'level' => 'nullable|string',
                'keyword' => 'nullable|string',
            ]);

            $filename = $request->input('filename');
            $page = (int) $request->input('page', 1);
            $pageSize = min((int) $request->input('page_size', 100), 500);
            $level = $request->input('level', '');
            $keyword = $request->input('keyword', '');

            $filePath = $this->logPath.DIRECTORY_SEPARATOR.$filename;

            if (! File::exists($filePath)) {
                return response()->json(ApiResponse::error(40401, '日志文件不存在'));
            }

            $realPath = realpath($filePath);
            $realLogPath = realpath($this->logPath);
            if (! $realPath || ! str_starts_with($realPath, $realLogPath)) {
                return response()->json(ApiResponse::error(40001, '非法的文件路径'));
            }

            $content = File::get($filePath);
            $lines = explode("\n", $content);

            $filteredLines = [];
            foreach ($lines as $line) {
                if (trim($line) === '') {
                    continue;
                }

                if ($level && ! $this->matchLevel($line, $level)) {
                    continue;
                }

                if ($keyword && stripos($line, $keyword) === false) {
                    continue;
                }

                $filteredLines[] = $this->parseLogLine($line);
            }

            $total = count($filteredLines);
            $filteredLines = array_reverse($filteredLines);
            $offset = ($page - 1) * $pageSize;
            $paginatedLines = array_slice($filteredLines, $offset, $pageSize);

            return response()->json([
                'code' => 0,
                'message' => 'success',
                'data' => $paginatedLines,
                'meta' => [
                    'total' => $total,
                    'current_page' => $page,
                    'per_page' => $pageSize,
                ],
                'timestamp' => time(),
            ]);
        } catch (\Exception $e) {
            return response()->json(ApiResponse::error(50001, '读取日志文件失败：'.$e->getMessage()));
        }
    }

    public function stats(Request $request)
    {
        try {
            $filename = $request->input('filename');
            $days = (int) $request->input('days', 7);

            if ($filename) {
                $stats = $this->getFileStats($filename);
            } else {
                $stats = $this->getMultiDayStats($days);
            }

            return response()->json(ApiResponse::success($stats));
        } catch (\Exception $e) {
            return response()->json(ApiResponse::error(50001, '获取日志统计失败：'.$e->getMessage()));
        }
    }

    public function destroy(Request $request)
    {
        try {
            $request->validate([
                'filename' => 'required|string',
            ]);

            $filename = $request->input('filename');
            $filePath = $this->logPath.DIRECTORY_SEPARATOR.$filename;

            if (! File::exists($filePath)) {
                return response()->json(ApiResponse::error(40401, '日志文件不存在'));
            }

            $realPath = realpath($filePath);
            $realLogPath = realpath($this->logPath);
            if (! $realPath || ! str_starts_with($realPath, $realLogPath)) {
                return response()->json(ApiResponse::error(40001, '非法的文件路径'));
            }

            $this->ensureWritable($filePath);
            File::delete($filePath);

            return response()->json(ApiResponse::success(null, '日志文件删除成功'));
        } catch (\Exception $e) {
            return response()->json(ApiResponse::error(50001, '删除日志文件失败：'.$e->getMessage()));
        }
    }

    public function clear(Request $request)
    {
        try {
            $request->validate([
                'filename' => 'required|string',
            ]);

            $filename = $request->input('filename');
            $filePath = $this->logPath.DIRECTORY_SEPARATOR.$filename;

            if (! File::exists($filePath)) {
                return response()->json(ApiResponse::error(40401, '日志文件不存在'));
            }

            $realPath = realpath($filePath);
            $realLogPath = realpath($this->logPath);
            if (! $realPath || ! str_starts_with($realPath, $realLogPath)) {
                return response()->json(ApiResponse::error(40001, '非法的文件路径'));
            }

            $this->ensureWritable($filePath);
            File::put($filePath, '');

            return response()->json(ApiResponse::success(null, '日志文件已清空'));
        } catch (\Exception $e) {
            return response()->json(ApiResponse::error(50001, '清空日志文件失败：'.$e->getMessage()));
        }
    }

    public function download(Request $request)
    {
        try {
            $request->validate([
                'filename' => 'required|string',
            ]);

            $filename = $request->input('filename');
            $filePath = $this->logPath.DIRECTORY_SEPARATOR.$filename;

            if (! File::exists($filePath)) {
                abort(404, '日志文件不存在');
            }

            $realPath = realpath($filePath);
            $realLogPath = realpath($this->logPath);
            if (! $realPath || ! str_starts_with($realPath, $realLogPath)) {
                abort(400, '非法的文件路径');
            }

            return response()->download($realPath, $filename);
        } catch (\Exception $e) {
            abort(500, '下载日志文件失败：'.$e->getMessage());
        }
    }

    protected function ensureWritable(string $filePath): void
    {
        if (is_writable($filePath)) {
            return;
        }

        if (! @chmod($filePath, 0666)) {
            throw new \RuntimeException('文件无写入权限，请检查 storage/logs 目录权限');
        }

        if (! is_writable($filePath)) {
            throw new \RuntimeException('文件无写入权限，请检查 storage/logs 目录权限');
        }
    }

    protected function formatFileSize(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $i = 0;
        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }

        return round($bytes, 2).' '.$units[$i];
    }

    protected function matchLevel(string $line, string $level): bool
    {
        $levelMap = [
            'debug' => 'DEBUG',
            'info' => 'INFO',
            'notice' => 'NOTICE',
            'warning' => 'WARNING',
            'error' => 'ERROR',
            'critical' => 'CRITICAL',
            'alert' => 'ALERT',
            'emergency' => 'EMERGENCY',
        ];

        $targetLevel = $levelMap[strtolower($level)] ?? strtoupper($level);

        return stripos($line, '.'.$targetLevel.':') !== false ||
               stripos($line, '['.$targetLevel.']') !== false;
    }

    protected function parseLogLine(string $line): array
    {
        $level = 'info';
        $levels = ['DEBUG', 'INFO', 'NOTICE', 'WARNING', 'ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY'];

        foreach ($levels as $lvl) {
            if (stripos($line, '.'.$lvl.':') !== false || stripos($line, '['.$lvl.']') !== false) {
                $level = strtolower($lvl);
                break;
            }
        }

        $datetime = '';
        if (preg_match('/\[(\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}[^\]]*)\]/', $line, $matches)) {
            $datetime = $matches[1];
        }

        return [
            'content' => $line,
            'level' => $level,
            'datetime' => $datetime,
        ];
    }

    protected function getFileStats(string $filename): array
    {
        $filePath = $this->logPath.DIRECTORY_SEPARATOR.$filename;
        $content = File::exists($filePath) ? File::get($filePath) : '';

        $stats = [
            'total_lines' => 0,
            'levels' => [
                'debug' => 0,
                'info' => 0,
                'notice' => 0,
                'warning' => 0,
                'error' => 0,
                'critical' => 0,
                'alert' => 0,
                'emergency' => 0,
            ],
        ];

        $lines = explode("\n", $content);
        $stats['total_lines'] = count(array_filter($lines, fn ($line) => trim($line) !== ''));

        foreach ($lines as $line) {
            foreach ($stats['levels'] as $level => $count) {
                if ($this->matchLevel($line, $level)) {
                    $stats['levels'][$level]++;
                    break;
                }
            }
        }

        return $stats;
    }

    protected function getMultiDayStats(int $days): array
    {
        $stats = [
            'days' => [],
            'total_errors' => 0,
            'total_warnings' => 0,
        ];

        for ($i = 0; $i < $days; $i++) {
            $date = date('Y-m-d', strtotime("-{$i} days"));
            $pattern = $this->logPath.DIRECTORY_SEPARATOR."*{$date}*.log";
            $files = glob($pattern);

            $dayStats = [
                'date' => $date,
                'errors' => 0,
                'warnings' => 0,
                'files' => count($files),
            ];

            foreach ($files as $file) {
                $content = File::get($file);
                $dayStats['errors'] += substr_count($content, '.ERROR:');
                $dayStats['errors'] += substr_count($content, '.CRITICAL:');
                $dayStats['errors'] += substr_count($content, '.ALERT:');
                $dayStats['errors'] += substr_count($content, '.EMERGENCY:');
                $dayStats['warnings'] += substr_count($content, '.WARNING:');
            }

            $stats['days'][] = $dayStats;
            $stats['total_errors'] += $dayStats['errors'];
            $stats['total_warnings'] += $dayStats['warnings'];
        }

        return $stats;
    }
}
