<?php

namespace App\Services;

use App\Models\EarnApiToken;
use App\Models\EarnTaskAssignment;
use App\Models\EarnTaskResult;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class EarnTaskResultService
{
    /**
     * Sonuç işleme olaylarını mevcut güvenli Earn access logger ile kaydeder.
     *
     * @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
     */
    public function __construct(private readonly EarnApiAccessLogger $logs) {}

    /**
     * Doğrulanmış token ve cihazdan gelen sonuçları sunucudaki atamaya göre idempotent kaydeder.
     *
     * @param  array<int, array<string, mixed>>  $items  Doğrulanmış görev sonucu nesneleri.
     * @return array{received: int, accepted: int, rejected: int, results: array<int, array<string, mixed>>}
     *
     * @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
     */
    public function store(Request $request, array $items): array
    {
        /**
         * @var EarnApiToken $token Middleware tarafından doğrulanmış API anahtarı kaydı.
         *
         * @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
         */
        $token = $request->attributes->get('earn_api_token');
        $deviceHash = (string) $request->attributes->get('earn_device_hash');
        $results = [];
        $accepted = 0;

        foreach ($items as $item) {
            // Atama doğrulaması ile ilk sonuç kaydını aynı kilitli işlemde tutar.
            // Yazar: Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
            $outcome = DB::transaction(function () use ($item, $token, $deviceHash): array {
                $assignment = EarnTaskAssignment::query()->where('task_id', $item['task_id'])->lockForUpdate()->first();
                if (! $assignment instanceof EarnTaskAssignment
                    || $assignment->personal_access_token_id !== $token->id
                    || ! hash_equals($assignment->device_id_hash, $deviceHash)) {
                    return ['accepted' => false, 'code' => 'TASK_NOT_ASSIGNED'];
                }
                if ($assignment->user_id !== $item['CID']) {
                    return ['accepted' => false, 'code' => 'CID_MISMATCH'];
                }

                $existing = EarnTaskResult::query()->where('earn_task_assignment_id', $assignment->id)->first();
                if ($existing instanceof EarnTaskResult) {
                    return ['accepted' => true, 'code' => 'ALREADY_RECORDED', 'duplicate' => true];
                }

                EarnTaskResult::query()->create([
                    'earn_task_assignment_id' => $assignment->id, 'user_id' => $assignment->user_id,
                    'earn_member_id' => $token->tokenable_id, 'personal_access_token_id' => $token->id,
                    'device_id_hash' => $deviceHash, 'completed' => $item['completed'],
                    'response_code' => $item['responseCode'], 'error_message' => $item['error'],
                    'submitted_at' => now(),
                ]);

                return ['accepted' => true, 'code' => null];
            }, 3);

            $results[] = ['task_id' => $item['task_id'], 'accepted' => $outcome['accepted'], 'code' => $outcome['code']];
            if ($outcome['accepted']) {
                $accepted++;
            }
            $event = match ($outcome['code']) {
                'ALREADY_RECORDED' => 'task_result_duplicate',
                'CID_MISMATCH' => 'task_result_cid_mismatch',
                'TASK_NOT_ASSIGNED' => 'task_result_not_assigned',
                default => 'task_result_accepted',
            };
            $this->logs->record($request, $event, $outcome['accepted'] ? 200 : 422, $outcome['code']);
            if (! $outcome['accepted']) {
                $this->logs->record($request, 'task_result_rejected', 422, $outcome['code']);
            }
        }

        return ['received' => count($items), 'accepted' => $accepted,
            'rejected' => count($items) - $accepted, 'results' => $results];
    }
}
