<?php

declare(strict_types=1);

namespace Tests\Support\Imitation;

use App\Services\DaData\DaDataException;
use App\Services\DaData\DaDataPhoneClient;
use App\Services\DaData\DaDataPhoneResponse;

/**
 * Детерминированный фейк DaData-клиента для тестов имитации (Phase 1).
 *
 * Позволяет прогонять каскад LeadRegionResolver без внешних HTTP-вызовов:
 * заранее регистрируем ответы по номеру телефона через stub(), затем
 * биндим этот фейк в контейнер вместо реального DaDataPhoneClient.
 *
 * Использование:
 *   $fake = new FakeDaDataPhoneClient();
 *   $fake->stub('79990000077', qc: 0, region: 'Москва', provider: 'МТС');
 *   app()->instance(DaDataPhoneClient::class, $fake);
 *
 * Task 1 — Phase 1 Portal Client Imitation Harness.
 * Spec: docs/superpowers/specs/2026-06-03-portal-client-imitation-phase1-design.md
 */
class FakeDaDataPhoneClient extends DaDataPhoneClient
{
    /**
     * @var array<string, DaDataPhoneResponse|null> phone => response (null = throw DaDataException)
     */
    private array $stubs = [];

    /**
     * Переопределяем конструктор без вызова parent, чтобы не требовать HttpFactory в тестах.
     */
    public function __construct() {}

    /**
     * Зарегистрировать детерминированный ответ для номера телефона.
     *
     * @param  int  $qc  Код качества DaData (0=хорошо, 1=не уточнён, 2=мусор, 3=изменён, 7=иностранец)
     */
    public function stub(
        string $phone,
        int $qc,
        ?string $region = null,
        ?string $provider = null,
    ): self {
        $this->stubs[$phone] = new DaDataPhoneResponse(
            qc: $qc,
            qcConflict: null,
            type: null,
            phone: $phone,
            provider: $provider,
            region: $region,
            city: null,
            timezone: null,
            raw: [
                'qc' => $qc,
                'provider' => $provider,
                'region' => $region,
                'phone' => $phone,
            ],
        );

        return $this;
    }

    /**
     * Зарегистрировать выброс DaDataException для номера телефона.
     * Используется для тестирования ветки деградации (Россвязь-fallback).
     */
    public function stubThrows(string $phone): self
    {
        $this->stubs[$phone] = null; // null = throw

        return $this;
    }

    /**
     * Возвращает заранее зарегистрированный ответ или бросает DaDataException.
     *
     * @throws DaDataException Если стаб не зарегистрирован или зарегистрирован как throw.
     */
    public function cleanPhone(string $phone): DaDataPhoneResponse
    {
        if (! array_key_exists($phone, $this->stubs)) {
            throw new DaDataException("FakeDaDataPhoneClient: no stub registered for phone {$phone}");
        }

        $response = $this->stubs[$phone];

        if ($response === null) {
            throw new DaDataException("FakeDaDataPhoneClient: stubbed to throw for phone {$phone}");
        }

        return $response;
    }
}
