<?php

declare(strict_types=1);

namespace Bot\Infrastructure\Repository\Database;

use Bot\Domain\Repository\LogRepositoryInterface;
use PDO;

final class DatabaseLogRepository implements LogRepositoryInterface
{
    public function __construct(
        private readonly PDO $pdo,
    ) {
    }

    public function log(
        string $level,
        string $message,
        array $context = [],
        ?string $file = null,
        ?int $line = null,
        ?int $telegramId = null,
        ?int $userId = null,
        ?int $adminId = null,
        ?int $mediaId = null,
        ?\Throwable $exception = null,
    ): void {
        try {
            // بررسی وجود جدول logs
            $stmt = $this->pdo->query("SHOW TABLES LIKE 'logs'");
            if ($stmt->rowCount() === 0) {
                // جدول وجود ندارد، فقط به error_log می‌نویسیم
                error_log(sprintf(
                    '[DatabaseLogRepository] جدول logs وجود ندارد. لطفاً migration را اجرا کنید. | Original: [%s] %s',
                    $level,
                    $message
                ));
                return;
            }

            $contextJson = !empty($context) ? json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
            $exceptionType = $exception !== null ? get_class($exception) : null;
            $exceptionMessage = $exception?->getMessage();
            $exceptionTrace = $exception !== null ? $exception->getTraceAsString() : null;

            $stmt = $this->pdo->prepare('
                INSERT INTO logs (
                    level, message, context, file, line,
                    telegram_id, user_id, admin_id, media_id,
                    exception_type, exception_message, exception_trace
                ) VALUES (
                    :level, :message, :context, :file, :line,
                    :telegram_id, :user_id, :admin_id, :media_id,
                    :exception_type, :exception_message, :exception_trace
                )
            ');

            $stmt->execute([
                'level' => $level,
                'message' => $message,
                'context' => $contextJson,
                'file' => $file,
                'line' => $line,
                'telegram_id' => $telegramId,
                'user_id' => $userId,
                'admin_id' => $adminId,
                'media_id' => $mediaId,
                'exception_type' => $exceptionType,
                'exception_message' => $exceptionMessage,
                'exception_trace' => $exceptionTrace,
            ]);
        } catch (\Throwable $e) {
            // اگر خطا در ذخیره لاگ رخ داد، به فایل لاگ می‌نویسیم
            error_log(sprintf(
                '[DatabaseLogRepository] خطا در ذخیره لاگ: %s | Original: [%s] %s',
                $e->getMessage(),
                $level,
                $message
            ));
        }
    }

    public function findLogs(
        ?string $level = null,
        ?\DateTimeImmutable $startDate = null,
        ?\DateTimeImmutable $endDate = null,
        ?int $telegramId = null,
        int $limit = 100,
        int $offset = 0,
    ): array {
        // بررسی وجود جدول logs
        $stmt = $this->pdo->query("SHOW TABLES LIKE 'logs'");
        if ($stmt->rowCount() === 0) {
            return [];
        }

        $conditions = [];
        $params = [];

        if ($level !== null) {
            $conditions[] = 'level = :level';
            $params['level'] = $level;
        }

        if ($startDate !== null) {
            $conditions[] = 'created_at >= :start_date';
            $params['start_date'] = $startDate->format('Y-m-d H:i:s');
        }

        if ($endDate !== null) {
            $conditions[] = 'created_at <= :end_date';
            $params['end_date'] = $endDate->format('Y-m-d H:i:s');
        }

        if ($telegramId !== null) {
            $conditions[] = 'telegram_id = :telegram_id';
            $params['telegram_id'] = $telegramId;
        }

        $whereClause = !empty($conditions) ? 'WHERE ' . implode(' AND ', $conditions) : '';
        $params['limit'] = $limit;
        $params['offset'] = $offset;

        $sql = "SELECT * FROM logs {$whereClause} ORDER BY created_at DESC LIMIT :limit OFFSET :offset";
        $stmt = $this->pdo->prepare($sql);

        foreach ($params as $key => $value) {
            $stmt->bindValue(':' . $key, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
        }

        $stmt->execute();
        return $stmt->fetchAll();
    }

    public function countLogs(
        ?string $level = null,
        ?\DateTimeImmutable $startDate = null,
        ?\DateTimeImmutable $endDate = null,
        ?int $telegramId = null,
    ): int {
        // بررسی وجود جدول logs
        $stmt = $this->pdo->query("SHOW TABLES LIKE 'logs'");
        if ($stmt->rowCount() === 0) {
            return 0;
        }

        $conditions = [];
        $params = [];

        if ($level !== null) {
            $conditions[] = 'level = :level';
            $params['level'] = $level;
        }

        if ($startDate !== null) {
            $conditions[] = 'created_at >= :start_date';
            $params['start_date'] = $startDate->format('Y-m-d H:i:s');
        }

        if ($endDate !== null) {
            $conditions[] = 'created_at <= :end_date';
            $params['end_date'] = $endDate->format('Y-m-d H:i:s');
        }

        if ($telegramId !== null) {
            $conditions[] = 'telegram_id = :telegram_id';
            $params['telegram_id'] = $telegramId;
        }

        $whereClause = !empty($conditions) ? 'WHERE ' . implode(' AND ', $conditions) : '';

        $sql = "SELECT COUNT(*) as count FROM logs {$whereClause}";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);

        $result = $stmt->fetch();
        return (int) ($result['count'] ?? 0);
    }

    public function deleteOldLogs(\DateTimeImmutable $beforeDate): int {
        $stmt = $this->pdo->prepare('DELETE FROM logs WHERE created_at < :before_date');
        $stmt->execute(['before_date' => $beforeDate->format('Y-m-d H:i:s')]);
        return $stmt->rowCount();
    }
}

