<?php

namespace App\Http\Controllers\Api\V1\Admin\Reconcile;

use App\Models\AdminSite;
use App\Models\AdminUser;
use App\Models\TransferOrder;
use App\Models\ReconcileJob;
use Illuminate\Http\Request;
use App\Models\ReconcileItem;
use App\Support\AdminAgentScope;
use App\Support\AdminApiList;
use App\Support\AdminReconcileScope;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminTransferOrderCapabilities;
use App\Services\Wallet\LotteryTransferService;
use App\Services\Wallet\HttpMainSiteWalletIdempotentProbeClient;
use App\Services\Wallet\MainSiteWalletIdempotentProbeResult;

/** GET /api/v1/admin/reconcile-jobs/{reconcile_job}/items */
final class ReconcileItemIndexController extends Controller
{
    public function __construct(
        private readonly LotteryTransferService $transferService,
        private readonly HttpMainSiteWalletIdempotentProbeClient $mainSiteProbeClient,
    ) {}

    public function __invoke(Request $request, ReconcileJob $reconcile_job): JsonResponse
    {
        $admin = $request->lotteryAdmin();
        abort_if($admin === null, 401);

        if ($denied = AdminReconcileScope::denyUnlessJobAccessible($admin, $reconcile_job)) {
            return $denied;
        }

        $p = AdminApiList::readPaging($request, 50, 200);

        $itemsQuery = $reconcile_job->items()->orderBy('id');
        if (! $admin->isSuperAdmin()) {
            $siteIds = $admin->accessibleAdminSiteIds() ?? [];
            $siteCodes = $siteIds === []
                ? []
                : AdminSite::query()
                    ->whereIn('id', $siteIds)
                    ->pluck('code')
                    ->map(static fn ($code): string => (string) $code)
                    ->all();
            $agent = AdminAgentScope::primaryAgentNode($admin);
            AdminReconcileScope::applyItemsTransferPlayerScope($itemsQuery, $siteCodes, $agent);
        }

        $paginator = $itemsQuery->paginate($p['perPage'], ['*'], 'page', $p['page']);

        $transferNos = collect($paginator->items())
            ->map(fn (ReconcileItem $item) => $item->side_a_ref)
            ->filter(fn ($value) => is_string($value) && $value !== '')
            ->values()
            ->all();

        $transferOrders = $transferNos === []
            ? collect()
            : TransferOrder::query()
                ->with(['player:id,site_code,site_player_id'])
                ->whereIn('transfer_no', $transferNos)
                ->get()
                ->keyBy('transfer_no');

        return AdminApiList::jsonWith($paginator, function (ReconcileItem $r) use ($transferOrders, $admin): array {
            $transferNo = (string) ($r->side_a_ref ?? '');
            /** @var TransferOrder|null $order */
            $order = $transferNo !== '' ? $transferOrders->get($transferNo) : null;
            $resolvedStatuses = ['success', 'reversed', 'manually_processed'];
            $currentStatus = $order?->status;
            $isResolved = $r->resolved_at !== null
                || in_array($currentStatus, $resolvedStatuses, true);

            $capabilities = $order instanceof TransferOrder
                ? AdminTransferOrderCapabilities::forOrder(
                    $order,
                    $admin,
                    $this->transferService,
                )
                : [
                    'can_reverse' => false,
                    'can_complete_credit' => false,
                    'can_manually_process' => false,
                ];

            $mainSiteCheck = $this->resolveMainSiteCheck($order);

            return [
                'id' => (int) $r->id,
                'side_a_ref' => $r->side_a_ref,
                'side_b_ref' => $r->side_b_ref,
                'external_ref_no' => $order?->external_ref_no,
                'difference_amount' => (int) $r->difference_amount,
                'status' => $r->status,
                'resolved_at' => $r->resolved_at?->toIso8601String(),
                'is_resolved' => $isResolved,
                'current_transfer_status' => $currentStatus,
                'transfer_direction' => $order?->direction,
                'transfer_fail_reason' => $order?->fail_reason,
                'main_site_check' => $mainSiteCheck['status'],
                'main_site_check_message' => $mainSiteCheck['message'],
                'main_site_external_ref_no' => $mainSiteCheck['external_ref_no'],
                ...$capabilities,
                'created_at' => $r->created_at?->toIso8601String(),
            ];
        }, [
            'job_id' => (int) $reconcile_job->id,
            'job_no' => $reconcile_job->job_no,
        ]);
    }

    /**
     * @return array{status: string, message: ?string, external_ref_no: ?string}
     */
    private function resolveMainSiteCheck(?TransferOrder $order): array
    {
        if ($order === null || $order->player === null) {
            return [
                'status' => MainSiteWalletIdempotentProbeResult::STATUS_SKIPPED,
                'message' => null,
                'external_ref_no' => null,
            ];
        }

        if (trim((string) $order->idempotent_key) === '') {
            return [
                'status' => MainSiteWalletIdempotentProbeResult::STATUS_SKIPPED,
                'message' => null,
                'external_ref_no' => null,
            ];
        }

        $shouldProbe = trim((string) $order->external_ref_no) !== ''
            || $order->status === 'success'
            || (
                $order->direction === 'in'
                && $order->status === 'pending_reconcile'
                && $order->fail_reason === 'lottery_credit_failed'
            );

        if (! $shouldProbe) {
            return [
                'status' => MainSiteWalletIdempotentProbeResult::STATUS_SKIPPED,
                'message' => null,
                'external_ref_no' => null,
            ];
        }

        $probe = $this->mainSiteProbeClient->probe($order->player, (string) $order->idempotent_key);

        return [
            'status' => $probe->status,
            'message' => $probe->message,
            'external_ref_no' => $probe->externalRefNo ?? $order->external_ref_no,
        ];
    }
}
