<?php
declare(strict_types=1);

namespace App\Modules\Automation;

use PDO;

final class AutomationExecutionLogRepository
{
    public function __construct(private readonly PDO $pdo)
    {
    }

    /**
     * @param array<string, mixed> $data
     */
    public function create(array $data): void
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO automation_execution_logs (
                event_type,
                rule_id,
                rule_name,
                order_id,
                execution_status,
                result_message,
                context_json,
                executed_at,
                created_at
            ) VALUES (
                :event_type,
                :rule_id,
                :rule_name,
                :order_id,
                :execution_status,
                :result_message,
                :context_json,
                :executed_at,
                NOW()
            )'
        );

        $statement->execute([
            'event_type' => (string) ($data['event_type'] ?? ''),
            'rule_id' => isset($data['rule_id']) ? (int) $data['rule_id'] : null,
            'rule_name' => (string) ($data['rule_name'] ?? ''),
            'order_id' => (int) ($data['order_id'] ?? 0),
            'execution_status' => (string) ($data['execution_status'] ?? ''),
            'result_message' => $this->trimNullable((string) ($data['result_message'] ?? '')),
            'context_json' => $this->encodeJson($data['context'] ?? null),
            'executed_at' => (string) ($data['executed_at'] ?? date('Y-m-d H:i:s')),
        ]);
    }

    /**
     * @param array<string, mixed> $filters
     * @return list<array<string, mixed>>
     */
    public function paginate(array $filters, int $page, int $perPage): array
    {
        $safePage = max(1, $page);
        $safePerPage = max(1, min(100, $perPage));
        $offset = ($safePage - 1) * $safePerPage;

        ['where' => $whereSql, 'params' => $params] = $this->buildFilters($filters);

        $sql = 'SELECT id, event_type, rule_id, rule_name, order_id, execution_status, result_message, context_json, executed_at
            FROM automation_execution_logs
            ' . $whereSql . '
            ORDER BY executed_at DESC, id DESC
            LIMIT :limit OFFSET :offset';

        $statement = $this->pdo->prepare($sql);
        foreach ($params as $key => $value) {
            $statement->bindValue(':' . $key, $value);
        }
        $statement->bindValue(':limit', $safePerPage, PDO::PARAM_INT);
        $statement->bindValue(':offset', $offset, PDO::PARAM_INT);
        $statement->execute();

        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        foreach ($rows as &$row) {
            $decoded = json_decode((string) ($row['context_json'] ?? ''), true);
            $row['context_json'] = is_array($decoded) ? $decoded : null;
        }
        unset($row);

        return $rows;
    }

    /**
     * @param array<string, mixed> $filters
     */
    public function count(array $filters): int
    {
        ['where' => $whereSql, 'params' => $params] = $this->buildFilters($filters);

        $statement = $this->pdo->prepare(
            'SELECT COUNT(*) FROM automation_execution_logs ' . $whereSql
        );
        $statement->execute($params);
        $value = $statement->fetchColumn();

        return max(0, (int) $value);
    }

    public function purgeOlderThanDays(int $days): int
    {
        $safeDays = max(1, min(3650, $days));
        $statement = $this->pdo->prepare(
            'DELETE FROM automation_execution_logs WHERE executed_at < DATE_SUB(NOW(), INTERVAL :days DAY)'
        );
        $statement->bindValue(':days', $safeDays, PDO::PARAM_INT);
        $statement->execute();

        return $statement->rowCount();
    }

    /**
     * @return list<string>
     */
    public function listEventTypes(): array
    {
        $statement = $this->pdo->query(
            'SELECT DISTINCT event_type FROM automation_execution_logs ORDER BY event_type ASC'
        );
        $rows = $statement->fetchAll(PDO::FETCH_COLUMN);
        if (!is_array($rows)) {
            return [];
        }

        return array_values(array_filter(array_map('strval', $rows), static fn (string $value): bool => $value !== ''));
    }

    /**
     * @param array<string, mixed> $filters
     * @return array{where:string,params:array<string,mixed>}
     */
    private function buildFilters(array $filters): array
    {
        $where = [];
        $params = [];

        $eventType = trim((string) ($filters['event_type'] ?? ''));
        if ($eventType !== '') {
            $where[] = 'event_type = :event_type';
            $params['event_type'] = $eventType;
        }

        $executionStatus = trim((string) ($filters['execution_status'] ?? ''));
        if ($executionStatus !== '') {
            $where[] = 'execution_status = :execution_status';
            $params['execution_status'] = $executionStatus;
        }

        $ruleId = (int) ($filters['rule_id'] ?? 0);
        if ($ruleId > 0) {
            $where[] = 'rule_id = :rule_id';
            $params['rule_id'] = $ruleId;
        }

        $orderId = (int) ($filters['order_id'] ?? 0);
        if ($orderId > 0) {
            $where[] = 'order_id = :order_id';
            $params['order_id'] = $orderId;
        }

        $dateFrom = trim((string) ($filters['date_from'] ?? ''));
        if ($dateFrom !== '') {
            $where[] = 'DATE(executed_at) >= :date_from';
            $params['date_from'] = $dateFrom;
        }

        $dateTo = trim((string) ($filters['date_to'] ?? ''));
        if ($dateTo !== '') {
            $where[] = 'DATE(executed_at) <= :date_to';
            $params['date_to'] = $dateTo;
        }

        if ($where === []) {
            return ['where' => '', 'params' => []];
        }

        return ['where' => 'WHERE ' . implode(' AND ', $where), 'params' => $params];
    }

    private function trimNullable(string $value): ?string
    {
        $trimmed = trim($value);
        if ($trimmed === '') {
            return null;
        }

        return mb_substr($trimmed, 0, 500);
    }

    private function encodeJson(mixed $data): ?string
    {
        if (!is_array($data) || $data === []) {
            return null;
        }

        return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: null;
    }
}
