<?php

namespace Tests\Unit;

use App\Models\Article;
use App\Services\CompanyApiService;
use App\Services\GuzzleHttpService;
use App\Services\SiteSyncResult;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Mockery;
use ReflectionClass;
use Tests\TestCase;

class CompanyApiServiceMansionSyncTest extends TestCase
{
    public function test_edit_mansion_returns_the_explicit_delivery_result(): void
    {
        $expected = SiteSyncResult::success(201);
        $http = Mockery::mock(GuzzleHttpService::class);
        $http->shouldReceive('postMansionRequest')
            ->once()
            ->with('mansion/edit/200', ['company_id' => 2, 'building_id' => 200])
            ->andReturn($expected);

        $result = (new CompanyApiService($http))->editMansion(200, [
            'company_id' => 2,
            'building_id' => 200,
        ]);

        $this->assertSame($expected, $result);
    }

    public function test_mansion_request_accepts_a_successful_2xx_response(): void
    {
        $result = $this->serviceWithResponses([new Response(204)])->editMansion(200, []);

        $this->assertTrue($result->isSuccessful());
        $this->assertSame(204, $result->statusCode());
    }

    public function test_mansion_request_converts_http_errors_to_safe_results(): void
    {
        foreach ([422, 500] as $statusCode) {
            $result = $this->serviceWithResponses([new Response($statusCode)])->editMansion(200, [
                'company_id' => 2,
                'building_id' => 200,
            ]);

            $this->assertFalse($result->isSuccessful());
            $this->assertSame($statusCode, $result->statusCode());
            $this->assertSame('Site synchronization failed.', $result->errorMessage());
        }
    }

    public function test_mansion_request_converts_timeouts_without_exposing_exception_text(): void
    {
        $exception = new ConnectException('secret timeout detail', new Request('POST', 'mansion/edit/200'));
        $result = $this->serviceWithResponses([$exception])->editMansion(200, []);

        $this->assertFalse($result->isSuccessful());
        $this->assertNull($result->statusCode());
        $this->assertSame('Site synchronization failed.', $result->errorMessage());
        $this->assertStringNotContainsString('secret', $result->errorMessage());
    }

    public function test_mansion_request_reports_missing_company_api_configuration(): void
    {
        $reflection = new ReflectionClass(GuzzleHttpService::class);
        $http = $reflection->newInstanceWithoutConstructor();

        $result = (new CompanyApiService($http))->editMansion(200, []);

        $this->assertFalse($result->isSuccessful());
        $this->assertNull($result->statusCode());
        $this->assertSame('Site API is not configured.', $result->errorMessage());
    }

    public function test_mansion_request_normalizes_invalid_legacy_dates(): void
    {
        $client = Mockery::mock(ClientInterface::class);
        $client->shouldReceive('request')
            ->once()
            ->with('POST', 'mansion/edit/200', Mockery::on(function (array $options): bool {
                return array_key_exists('conf_day', $options['json'])
                    && $options['json']['conf_day'] === null
                    && $options['json']['created_at'] === '2026-09-04'
                    && $options['json']['close_date'] === null;
            }))
            ->andReturn(new Response(200));

        $result = (new CompanyApiService(new GuzzleHttpService($client)))->editMansion(200, [
            'conf_day' => '0000-00-00',
            'created_at' => '2026-09-04',
            'close_date' => '1970-01-01 00:00:00 UTC',
        ]);

        $this->assertTrue($result->isSuccessful());
    }

    public function test_generic_post_request_still_accepts_an_eloquent_model(): void
    {
        $article = new Article;
        $article->forceFill([
            'building_id' => 200,
            'conf_day' => '0000-00-00',
        ]);
        $client = Mockery::mock(ClientInterface::class);
        $client->shouldReceive('request')
            ->once()
            ->with('POST', 'house/edit/200', Mockery::on(function (array $options): bool {
                return $options['json'] instanceof Article
                    && $options['json']['conf_day'] === null;
            }))
            ->andReturn(new Response(200, [], 'ok'));

        $response = (new GuzzleHttpService($client))->postRequest('house/edit/200', $article);

        $this->assertSame('ok', (string) $response);
    }

    private function serviceWithResponses(array $responses): CompanyApiService
    {
        $handler = HandlerStack::create(new MockHandler($responses));
        $client = new Client(['handler' => $handler]);

        return new CompanyApiService(new GuzzleHttpService($client));
    }
}
