<?php

namespace Tests\Feature\Modules\Segments\ControllersLogic;

use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\Segments\ControllersLogic\UpdateConstantLogic;
use App\Classes\Modules\Segments\DataTransferObjects\ConstantObject;
use App\Classes\Modules\Segments\Services\CreatesConstant;
use App\Classes\Modules\Segments\Services\FetchesConstant;
use App\Classes\Modules\Segments\Services\FetchesSegment;
use App\Classes\Modules\Segments\Services\UpdatesConstant;
use App\Classes\Modules\Segments\Standards\Rules\CanCreateConstant;
use App\Classes\Modules\Segments\Standards\Rules\CanUpdateConstant;
use App\Models\Segment;
use App\Models\SegmentConstant;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Bus;
use App\Classes\Jobs\UserRiskAnalysis;
use PHPUnit\Framework\Attributes\Group;
use Tests\TestCase;
use Mockery;

#[Group('feature')]
#[Group('segments')]
#[Group('logic')]
#[Group('ok_to_run')]
class UpdateConstantLogicTest extends TestCase
{
    //cief todo: 137 - User of type 3 should not be able to update or create constants
    // use DatabaseTransactions;

    private $user;
    private $segment;

    protected function setUp(): void
    {
        parent::setUp();

        $email = 'UpdateConstantLogicTest@example.com';
        $this->user = User::where('email', $email)->first();
        if (!$this->user) {
            $this->user = new User();
            $this->user->name = 'UpdateConstantLogicTest';
            $this->user->email = $email;
            $this->user->password = bcrypt('password');
            $this->user->save();
        }
        $this->actingAs($this->user);

        $this->segment = new Segment();
        $this->segment->id = 1;
        $this->segment->name = 'UpdateConstantLogicTest_Segment';
    }

    /**
     * Test UpdateConstantLogic execution for updating existing constant.
     *
     * @return void
     */
    public function test_update_constant_logic_updates_existing()
    {
        // A R R A N G E
        Bus::fake([UserRiskAnalysis::class]);

        $constant = new SegmentConstant();
        $constant->id = 10;
        $constant->segment_id = 1;
        $constant->name = 'test_update_constant_logic_updates_existing';
        $constant->reference = 'test_update_constant_logic_updates_existing';
        $constant->detail = (object) ['foo' => 'bar'];

        $fetchesSegment = Mockery::mock(FetchesSegment::class);
        $fetchesSegment->shouldReceive('execute')->with(['id' => 1])->andReturn($this->segment);

        $fetchesConstant = Mockery::mock(FetchesConstant::class);
        $fetchesConstant->shouldReceive('execute')->with(['segment_id' => 1, 'reference' => 'test_update_constant_logic_updates_existing'])->andReturn($constant);

        $canUpdateConstant = Mockery::mock(CanUpdateConstant::class);
        $canUpdateConstant->shouldReceive('passes')->once()->andReturn(true);

        $updatesConstant = Mockery::mock(UpdatesConstant::class);
        $updatesConstant->shouldReceive('execute')->once()->andReturn($constant);

        $canCreateConstant = Mockery::mock(CanCreateConstant::class);
        $createsConstant = Mockery::mock(CreatesConstant::class);

        $logic = new UpdateConstantLogic(
            $canUpdateConstant,
            $updatesConstant,
            $fetchesSegment,
            $fetchesConstant,
            $canCreateConstant,
            $createsConstant
        );

        $request = Request::create('/api/segments/1/constants', 'POST', [
            'name' => 'test_update_constant_logic_updates_existing_NEW',
            'reference' => 'test_update_constant_logic_updates_existing',
            'detail' => ['new' => 'data']
        ]);
        $request->headers->set('captcha-token', 'fake-token');
        $request->setRouteResolver(function () {
            $route = Mockery::mock(\Illuminate\Routing\Route::class);
            $route->shouldReceive('parameter')->with('id', null)->andReturn(1);
            return $route;
        });

        // A C T
        $response = $logic->execute($request);

        // A S S E R T
        $this->assertEquals(200, $response->getStatusCode());
        $data = $response->getData(true);
        $this->assertEquals('Updated Segment Constant Successful', $data['title']);
    }

    /**
     * Test UpdateConstantLogic execution for creating new constant if not found.
     *
     * @return void
     */
    public function test_update_constant_logic_creates_new_if_not_found()
    {
        // A R R A N G E
        Bus::fake([UserRiskAnalysis::class]);

        $fetchesSegment = Mockery::mock(FetchesSegment::class);
        $fetchesSegment->shouldReceive('execute')->andReturn($this->segment);

        $fetchesConstant = Mockery::mock(FetchesConstant::class);
        $fetchesConstant->shouldReceive('execute')->andThrow(new ResourceNotFoundException());

        $canUpdateConstant = Mockery::mock(CanUpdateConstant::class);
        $updatesConstant = Mockery::mock(UpdatesConstant::class);

        $canCreateConstant = Mockery::mock(CanCreateConstant::class);
        $canCreateConstant->shouldReceive('passes')->once()->andReturn(true);

        $constant = new SegmentConstant();
        $constant->id = 11;
        $constant->name = 'test_update_constant_logic_creates_new_if_not_found';

        $createsConstant = Mockery::mock(CreatesConstant::class);
        $createsConstant->shouldReceive('execute')->once()->andReturn($constant);

        $logic = new UpdateConstantLogic(
            $canUpdateConstant,
            $updatesConstant,
            $fetchesSegment,
            $fetchesConstant,
            $canCreateConstant,
            $createsConstant
        );

        $request = Request::create('/api/segments/1/constants', 'POST', [
            'name' => 'test_update_constant_logic_creates_new_if_not_found',
            'reference' => 'test_update_constant_logic_creates_new_if_not_found',
            'detail' => [],
        ]);
        $request->headers->set('captcha-token', 'fake-token');
        $request->setRouteResolver(function () {
            $route = Mockery::mock(\Illuminate\Routing\Route::class);
            $route->shouldReceive('parameter')->with('id', null)->andReturn(1);
            return $route;
        });

        // A C T
        $response = $logic->execute($request);

        // A S S E R T
        $this->assertEquals(200, $response->getStatusCode());
        $data = $response->getData(true);
        $this->assertEquals('Updated Segment Constant Successful', $data['title']);
    }

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
}
