<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Exceptions\SupportCenter\SupportCenterException;
use InvalidArgumentException;

/**
 * 接入码确认（Support Center task_no / access code）。
 */
final class HostSiteOnboardingConfirmService
{
    public function __construct(
        private readonly SupportCenterOpenApiTransport $transport,
        private readonly SupportCenterRequestContextBuilder $contextBuilder,
        private readonly HostSiteOnboardingLastAttemptStore $lastAttemptStore,
        private readonly HostSiteOnboardingStatusCacheStore $statusCacheStore,
        private readonly HostSiteOnboardingStatusPresenter $statusPresenter,
        private readonly HostSiteVerificationStaticPublisher $siteVerificationStaticPublisher,
        private readonly SupportCenterAuditLogger $auditLogger,
    ) {}

    /**
     * @return array<string, mixed>
     */
    public function confirm(string $accessCode): array
    {
        $taskNo = trim($accessCode);

        if ($taskNo === '') {
            throw new InvalidArgumentException((string) __('dberp.site_onboarding.errors.access_code_required'));
        }

        $payload = $this->compactPayload($this->contextBuilder->siteOnboardingConfirmBody($taskNo));

        try {
            $responseData = $this->transport->post(
                SupportCenterOpenHostEndpoints::SITE_BINDING_ACCESS_CONFIRM,
                $payload,
                'site_onboarding_confirm',
            );

            $normalized = $this->normalizeFromResponse($responseData, $taskNo);

            $this->lastAttemptStore->store([
                'attempted_at' => now()->utc()->toIso8601String(),
                'task_no' => $taskNo,
                'success' => true,
                'response' => $responseData,
            ]);

            $this->statusCacheStore->store($normalized);
            $this->siteVerificationStaticPublisher->publish();

            $this->auditLogger->logSiteAccessConfirmSucceeded($normalized, $taskNo);

            return $normalized;
        } catch (SupportCenterException $exception) {
            $this->lastAttemptStore->store([
                'attempted_at' => now()->utc()->toIso8601String(),
                'task_no' => $taskNo,
                'success' => false,
                'error_code' => $exception->errorCode(),
                'message' => $exception->userFacingMessage(),
                'request_id' => $exception->requestId(),
            ]);

            $this->auditLogger->logSiteAccessConfirmFailed($taskNo, $exception);

            throw $exception;
        }
    }

    /**
     * @param  array<string, mixed>  $responseData
     * @return array<string, mixed>
     */
    private function normalizeFromResponse(array $responseData, string $taskNo): array
    {
        $payload = $this->unwrapStatusPayload($responseData);
        $snapshot = $this->statusPresenter->normalizeFromOpenHostData($payload);

        return [
            'fetched_at' => now()->utc()->toIso8601String(),
            'task_no' => $this->statusPresenter->extractTaskNo($payload) ?? $taskNo,
            'site_no' => $this->statusPresenter->extractSiteNo($payload),
            'binding_status' => $snapshot['binding_status'],
            'binding_status_label' => $snapshot['binding_status_label'],
            'is_bound' => $snapshot['is_bound'],
            'next_action' => $snapshot['next_action'],
            'next_action_hint' => $snapshot['next_action_hint'],
            'data' => $responseData,
        ];
    }

    /**
     * access-confirm 可能附带 status_snapshot 子对象，优先合并顶层 Open Host 字段。
     *
     * @param  array<string, mixed>  $responseData
     * @return array<string, mixed>
     */
    private function unwrapStatusPayload(array $responseData): array
    {
        $nested = $responseData['status_snapshot'] ?? null;

        if (! is_array($nested)) {
            return $responseData;
        }

        return array_merge($nested, array_filter(
            $responseData,
            static fn (mixed $value, string $key): bool => $key !== 'status_snapshot' && $value !== null && $value !== '',
            ARRAY_FILTER_USE_BOTH,
        ));
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>
     */
    private function compactPayload(array $payload): array
    {
        return array_filter(
            $payload,
            static fn (mixed $value): bool => $value !== null && $value !== '',
        );
    }
}
