<?php

declare(strict_types=1);

namespace App\Repositories\Uploads;

use App\DTOs\Uploads\UploadSessionData;
use App\Services\Uploads\UploadSessionStatus;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\DB;
use RuntimeException;

final class UploadSessionRepository
{
    public function create(string $id, int $userId, string $tempPath, string $status, string $ip): UploadSessionData
    {
        $createdAt = CarbonImmutable::now();

        DB::table('uploads_sessions')->insert([
            'id' => $id,
            'user_id' => $userId,
            'temp_path' => $tempPath,
            'status' => $status,
            'ip' => $ip,
            'created_at' => $createdAt->toDateTimeString(),
            'progress' => 0,
            'failure_reason' => null,
        ]);

        return new UploadSessionData($id, $userId, $tempPath, $status, $ip, $createdAt, 0, null);
    }

    public function get(string $id): ?UploadSessionData
    {
        $row = DB::table('uploads_sessions')->where('id', $id)->first();

        if (! $row) {
            return null;
        }

        return new UploadSessionData(
            (string) $row->id,
            (int) $row->user_id,
            (string) $row->temp_path,
            (string) $row->status,
            (string) $row->ip,
            CarbonImmutable::parse($row->created_at),
            (int) ($row->progress ?? 0),
            $row->failure_reason ? (string) $row->failure_reason : null
        );
    }

    public function getOrFail(string $id): UploadSessionData
    {
        $session = $this->get($id);

        if (! $session) {
            throw new RuntimeException('Upload session not found.');
        }

        return $session;
    }

    public function updateStatus(string $id, string $status): void
    {
        DB::table('uploads_sessions')->where('id', $id)->update([
            'status' => $status,
        ]);
    }

    public function updateProgress(string $id, int $progress): void
    {
        DB::table('uploads_sessions')->where('id', $id)->update([
            'progress' => max(0, min(100, $progress)),
        ]);
    }

    public function updateFailureReason(string $id, ?string $reason): void
    {
        DB::table('uploads_sessions')->where('id', $id)->update([
            'failure_reason' => $reason,
        ]);
    }

    public function updateTempPath(string $id, string $tempPath): void
    {
        DB::table('uploads_sessions')->where('id', $id)->update([
            'temp_path' => $tempPath,
        ]);
    }

    public function countActiveForUser(int $userId): int
    {
        $terminal = [
            UploadSessionStatus::PROCESSED,
            UploadSessionStatus::QUARANTINED,
            UploadSessionStatus::CANCELLED,
        ];

        return (int) DB::table('uploads_sessions')
            ->where('user_id', $userId)
            ->whereNotIn('status', $terminal)
            ->count();
    }

    public function countForUserSince(int $userId, CarbonImmutable $since): int
    {
        return (int) DB::table('uploads_sessions')
            ->where('user_id', $userId)
            ->where('created_at', '>=', $since->toDateTimeString())
            ->count();
    }
}
