<?php

namespace MintyPHP\Repository\Scheduler;

use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus;
use MintyPHP\Domain\Taxonomy\ScheduledJobTriggerType;
use MintyPHP\Repository\Support\RepoQuery;

class ScheduledJobRunRepository implements ScheduledJobRunRepositoryInterface
{
    public function create(array $data): int|false
    {
        $id = DB::insert(
            'insert into scheduled_job_runs (
                run_uuid, job_id, job_key, trigger_type, status, actor_user_id,
                started_at, finished_at, duration_ms, error_code, error_message, result_json
            ) values (?,?,?,?,?,?,?,?,?,?,?,?)',
            (string) ($data['run_uuid'] ?? ''),
            (string) ((int) ($data['job_id'] ?? 0)),
            (string) ($data['job_key'] ?? ''),
            (string) ($data['trigger_type'] ?? ScheduledJobTriggerType::Scheduler->value),
            (string) ($data['status'] ?? ScheduledJobRunStatus::Failed->value),
            ($data['actor_user_id'] ?? null) !== null ? (string) ((int) $data['actor_user_id']) : null,
            (string) ($data['started_at'] ?? ''),
            $data['finished_at'] ?? null,
            ($data['duration_ms'] ?? null) !== null ? (string) ((int) $data['duration_ms']) : null,
            $data['error_code'] ?? null,
            $data['error_message'] ?? null,
            $data['result_json'] ?? null
        );
        return $id ? (int) $id : false;
    }

    public function listPagedByJobId(int $jobId, array $filters): array
    {
        if ($jobId <= 0) {
            return ['total' => 0, 'rows' => []];
        }

        $search = trim((string) ($filters['search'] ?? ''));
        $status = strtolower(trim((string) ($filters['status'] ?? '')));
        $triggerType = strtolower(trim((string) ($filters['trigger_type'] ?? '')));

        [$limit, $offset] = RepoQuery::sanitizeLimitOffset($filters, 20, 1, 200, 0);
        [$order, $dir] = RepoQuery::sanitizeOrder(
            $filters,
            ['id', 'started_at', 'finished_at', 'duration_ms', 'status', 'trigger_type'],
            'started_at',
            'desc'
        );

        $where = ['scheduled_job_runs.job_id = ?'];
        $params = [(string) $jobId];
        RepoQuery::addLikeFilter(
            $where,
            $params,
            ['scheduled_job_runs.run_uuid', 'scheduled_job_runs.error_code', 'scheduled_job_runs.error_message'],
            $search
        );
        if (ScheduledJobRunStatus::tryNormalize($status) !== null) {
            $where[] = 'scheduled_job_runs.status = ?';
            $params[] = $status;
        }
        if (ScheduledJobTriggerType::tryNormalize($triggerType) !== null) {
            $where[] = 'scheduled_job_runs.trigger_type = ?';
            $params[] = $triggerType;
        }

        $whereSql = ' where ' . implode(' and ', $where);
        $fromSql = ' from scheduled_job_runs left join users on users.id = scheduled_job_runs.actor_user_id ';

        $total = (int) (DB::selectValue('select count(*)' . $fromSql . $whereSql, ...$params) ?? 0);
        $rows = DB::select(
            'select
                scheduled_job_runs.id,
                scheduled_job_runs.run_uuid,
                scheduled_job_runs.job_id,
                scheduled_job_runs.job_key,
                scheduled_job_runs.trigger_type,
                scheduled_job_runs.status,
                scheduled_job_runs.actor_user_id,
                scheduled_job_runs.started_at,
                scheduled_job_runs.finished_at,
                scheduled_job_runs.duration_ms,
                scheduled_job_runs.error_code,
                scheduled_job_runs.error_message,
                scheduled_job_runs.result_json,
                scheduled_job_runs.created_at,
                users.id,
                users.uuid,
                users.display_name,
                users.email
            ' . $fromSql . $whereSql .
            sprintf(' order by scheduled_job_runs.`%s` %s limit ? offset ?', $order, $dir),
            ...array_merge($params, [(string) $limit, (string) $offset])
        );

        $normalized = [];
        if (is_array($rows)) {
            foreach ($rows as $row) {
                $item = $this->normalizeRow($row);
                if ($item !== null) {
                    $normalized[] = $item;
                }
            }
        }

        return ['total' => $total, 'rows' => $normalized];
    }

    public function purgeOlderThanDays(int $days): int
    {
        if ($days <= 0) {
            return 0;
        }
        $cutoff = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))
            ->modify('-' . $days . ' days')
            ->format('Y-m-d H:i:s');
        $deleted = DB::delete('delete from scheduled_job_runs where created_at < ?', $cutoff);
        return is_int($deleted) ? $deleted : 0;
    }

    private function normalizeRow(mixed $row): ?array
    {
        if (!is_array($row)) {
            return null;
        }
        $item = $row['scheduled_job_runs'] ?? [];
        if (!is_array($item) || !isset($item['id'])) {
            return null;
        }
        $user = is_array($row['users'] ?? null) ? $row['users'] : [];
        $item['actor_user_uuid'] = (string) ($user['uuid'] ?? '');
        $item['actor_user_display_name'] = (string) ($user['display_name'] ?? '');
        $item['actor_user_email'] = (string) ($user['email'] ?? '');
        return $item;
    }
}
