<?php

namespace Binaryk\LaravelRestify\Tests\Controllers;

use Binaryk\LaravelRestify\Fields\Image;
use Binaryk\LaravelRestify\Restify;
use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
use Binaryk\LaravelRestify\Tests\Fixtures\User\UserRepository;
use Binaryk\LaravelRestify\Tests\IntegrationTestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Testing\Fluent\AssertableJson;

class ProfileControllerTest extends IntegrationTestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->authenticate(User::factory()->create([
            'name' => 'Eduard Lupacescu',
            'email' => 'eduard.lupacescu@binarcode.com',
        ]));

        $this->mockPosts(
            $this->authenticatedAs->id
        );
    }

    public function test_profile_returns_authenticated_user(): void
    {
        $this->withoutExceptionHandling();

        $response = $this->getJson(Restify::path('profile'))
            ->assertOk()
            ->assertJsonStructure([
                'data',
            ]);

        $response->assertJsonFragment([
            'email' => $this->authenticatedAs->email,
        ]);
    }

    public function test_profile_returns_authenticated_user_with_related_posts(): void
    {
        $this->getJson(Restify::path('profile', [
            'related' => 'posts',
        ]))
            ->assertJson(fn ($json) => $json
                ->where('data.attributes.email', $this->authenticatedAs->email)
                ->has('data.relationships.posts')
                ->etc()
            );
    }

    public function test_profile_update()
    {
        $response = $this->putJson(Restify::path('profile'), [
            'email' => 'contact@binarschool.com',
            'name' => 'Eduard',
        ])->assertOk();

        $response->assertJsonFragment([
            'email' => 'contact@binarschool.com',
            'name' => 'Eduard',
        ]);
    }

    public function test_profile_update_password()
    {
        $this->putJson(Restify::path('profile'), [
            'email' => 'contact@binarschool.com',
            'name' => 'Eduard',
            'password' => 'secret',
            'password_confirmation' => 'secret',
        ])->assertOk();

        $this->assertTrue(Hash::check('secret', $this->authenticatedAs->password));
    }

    public function test_profile_update_unique_email(): void
    {
        User::factory()->create([
            'email' => 'existing@gmail.com',
        ]);

        $this->putJson(Restify::path('profile'), [
            'email' => 'existing@gmail.com',
            'name' => 'Eduard',
        ])->assertStatus(422);
    }

    public function test_profile_validation_from_repository(): void
    {
        UserRepository::$canUseForProfileUpdate = true;

        $this->putJson(Restify::path('profile'), [
            'email' => 'contact@binarschool.com',
            'name' => 'Ed',
        ])
            ->assertStatus(422)
            ->assertJson(
                fn (AssertableJson $json) => $json
                    ->has('message')
                    ->has('errors')
            );
    }

    public function test_get_profile_can_use_repository(): void
    {
        UserRepository::$canUseForProfile = true;

        $this->getJson(Restify::path('profile'))
            ->assertOk()
            ->assertJson(
                fn (AssertableJson $json) => $json
                    ->has('data')
                    ->where('data.attributes.email', $this->authenticatedAs->email)
                    ->etc()
            );
    }

    public function test_profile_returns_authenticated_user_with_related_posts_via_repository(): void
    {
        UserRepository::$canUseForProfile = true;

        $this->getJson(Restify::path('profile', [
            'related' => 'posts',
        ]))
            ->assertOk()
            ->assertJson(
                fn (AssertableJson $json) => $json
                    ->has('data')
                    ->has('data.attributes')
                    ->has('data.relationships.posts')
                    ->where('data.attributes.email', $this->authenticatedAs->email)
                    ->etc()
            );
    }

    public function test_profile_returns_authenticated_user_with_meta_profile_data_via_repository(): void
    {
        UserRepository::$canUseForProfile = true;

        UserRepository::$metaProfile = [
            'roles' => '',
        ];

        $this->getJson(Restify::path('profile'))
            ->assertJson(
                fn (AssertableJson $json) => $json
                    ->has('data.attributes')
                    ->has('data.meta.roles')
                    ->etc()
            );
    }

    public function test_profile_update_via_repository(): void
    {
        UserRepository::$canUseForProfileUpdate = true;

        $this->putJson(Restify::path('profile'), [
            'email' => $email = 'contact@binarschool.com',
        ])
            ->assertJson(
                fn (AssertableJson $json) => $json
                    ->where('data.attributes.email', $email)
            );
    }

    public function test_can_upload_avatar(): void
    {
        Storage::fake('customDisk');

        UserRepository::partialMock()
            ->shouldReceive('canUseForProfileUpdate')
            ->andReturnTrue()
            ->shouldReceive('fields')
            ->andReturn([
                field('name'),
                field('avatar_size'),
                field('avatar_original'),

                Image::make('avatar')
                    ->rules('required')
                    ->disk('customDisk')
                    ->storeOriginalName('avatar_original')
                    ->storeSize('avatar_size')
                    ->storeAs('avatar.jpg'),
            ]);

        $this->postJson(Restify::path('profile'), [
            'avatar' => UploadedFile::fake()->image('image.jpg'),
        ])->assertOk()->assertJsonFragment([
            'avatar_original' => 'image.jpg',
            'avatar' => '/storage/avatar.jpg',
        ]);

        Storage::disk('customDisk')->assertExists('avatar.jpg');
    }
}
