<?php

namespace Tests\Feature;

use App\Http\Controllers\Admin\ArticleController;
use App\Http\Controllers\Admin\Ajax\AjaxController;
use App\Http\Requests\MansionPost;
use App\Services\CompanyApiService;
use App\Services\MansionRoomSynchronizer;
use App\Services\MansionSiteSyncService;
use App\Services\SiteSyncResult;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Schema;
use Illuminate\Http\Request;
use Mockery;
use Tests\TestCase;

class MansionRoomSynchronizerTest extends TestCase
{
    private const COMPANY_ID = 10;

    private const MANSION_ID = 200;

    private const APPROVED_ADDITIONAL_FIELDS = [
        'name_apartment', 'pet_count', 'leasehold_kind', 'leasehold_period', 'leasehold_period_year',
        'leasehold_period_month', 'leasehold_period_year1', 'leasehold_period_month1', 'right_cost', 'right_cost_val',
        'deposit_cost', 'deposit_cost_val', 'security_deposit_cost', 'security_deposit_cost_val', 'other_leasehold',
        'land_area_val', 'kokudoho', 'gas', 'move_in_conditions',
    ];

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

        config()->set('database.default', 'sqlite');
        config()->set('database.connections.sqlite.database', ':memory:');
        DB::purge('sqlite');

        Schema::create('articles', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('company_id');
            $table->unsignedBigInteger('building_id')->unique();
            $table->unsignedBigInteger('mansion_id')->nullable();
            $table->string('del')->nullable();
            $table->string('zip')->nullable();
            $table->string('name_apartment')->nullable();
            $table->string('property_sub')->nullable();
            $table->string('property')->nullable();
            $table->string('name')->nullable();
            $table->text('memo1')->nullable();
            $table->text('memo2')->nullable();
            $table->text('build_note')->nullable();
            $table->text('comment')->nullable();
            $table->unsignedInteger('disp')->nullable();
            $table->unsignedInteger('first')->nullable();
            $table->unsignedInteger('library')->default(0);

            foreach (self::APPROVED_ADDITIONAL_FIELDS as $field) {
                if ($field !== 'name_apartment') {
                    $table->string($field)->nullable();
                }
            }

            $table->timestamps();
        });

        foreach (['relation_shareds', 'relation_lawrestrictions', 'relation_otherrestrictions'] as $tableName) {
            Schema::create($tableName, function (Blueprint $table) {
                $table->increments('id');
                $table->unsignedInteger('company_id')->nullable();
                $table->unsignedBigInteger('article_id');
                $table->unsignedInteger('item_id');
            });
        }
        Schema::create('relation_event_days', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('company_id')->nullable();
            $table->unsignedBigInteger('article_id');
            $table->date('event_day');
        });
    }

    public function test_it_scopes_active_rooms_to_the_resolved_company_and_mansion(): void
    {
        $this->insertRoom(['building_id' => 1001, 'del' => '0']);
        $this->insertRoom([
            'building_id' => 1002,
            'company_id' => 11,
            'zip' => 'other-company-zip',
        ]);
        $this->insertRoom([
            'building_id' => 1003,
            'mansion_id' => 201,
            'zip' => 'other-mansion-zip',
        ]);
        $this->insertRoom([
            'building_id' => 1004,
            'del' => '1',
            'zip' => 'deleted-room-zip',
        ]);

        $updated = (new MansionRoomSynchronizer)->sync(self::COMPANY_ID, self::MANSION_ID, [
            'zip' => 'updated-zip',
            'name' => 'Updated Mansion',
        ]);

        $this->assertSame(1, $updated);
        $this->assertDatabaseHas('articles', [
            'building_id' => 1001,
            'zip' => 'updated-zip',
            'name_apartment' => 'Updated Mansion',
        ]);
        foreach ([
            1002 => 'other-company-zip',
            1003 => 'other-mansion-zip',
            1004 => 'deleted-room-zip',
        ] as $buildingId => $zip) {
            $this->assertDatabaseHas('articles', [
                'building_id' => $buildingId,
                'zip' => $zip,
            ]);
        }
    }

    public function test_it_preserves_room_owned_fields_while_propagating_null_and_empty_values(): void
    {
        $this->insertRoom([
            'building_id' => 1005,
            'gas' => '3',
            'move_in_conditions' => 'Old condition',
            'property_sub' => 'room subtype',
            'property' => '6',
            'name' => 'Room 501',
            'memo1' => 'room memo 1',
            'memo2' => 'room memo 2',
            'build_note' => 'room build note',
            'comment' => 'room comment',
        ]);

        $updated = (new MansionRoomSynchronizer)->sync(self::COMPANY_ID, self::MANSION_ID, [
            'name' => 'Mansion Name',
            'gas' => null,
            'move_in_conditions' => '',
        ]);

        $this->assertSame(1, $updated);
        $room = DB::table('articles')->where('building_id', 1005)->first();

        $this->assertSame('Mansion Name', $room->name_apartment);
        $this->assertNull($room->gas);
        $this->assertSame('', $room->move_in_conditions);
        $this->assertSame('room subtype', $room->property_sub);
        $this->assertSame('6', $room->property);
        $this->assertSame('Room 501', $room->name);
        $this->assertSame('room memo 1', $room->memo1);
        $this->assertSame('room memo 2', $room->memo2);
        $this->assertSame('room build note', $room->build_note);
        $this->assertSame('room comment', $room->comment);
    }

    public function test_controller_uses_the_authenticated_company_at_the_service_boundary(): void
    {
        $this->insertRoom(['building_id' => self::MANSION_ID, 'mansion_id' => null]);
        Auth::shouldReceive('check')->andReturn(false);
        Auth::shouldReceive('user')->once()->andReturn((object) ['company_id' => self::COMPANY_ID]);

        $roomSynchronizer = Mockery::mock(MansionRoomSynchronizer::class);
        $roomSynchronizer->shouldReceive('sync')
            ->once()
            ->with(self::COMPANY_ID, self::MANSION_ID, Mockery::on(function (array $payload): bool {
                return $payload['company_id'] === self::COMPANY_ID
                    && $payload['name'] === 'Updated Mansion';
            }))
            ->andReturn(0);

        $siteSyncService = Mockery::mock(MansionSiteSyncService::class);
        $siteSyncService->shouldReceive('sendOrQueue')
            ->once()
            ->with(self::COMPANY_ID, self::MANSION_ID, Mockery::type('array'))
            ->andReturn(SiteSyncResult::success(200));
        $controller = Mockery::mock(ArticleController::class)->makePartial();
        $controller->shouldReceive('setCount')->once();
        $request = MansionPost::create('/admin/editMansionSave/' . self::MANSION_ID, 'POST', [
            'id' => self::MANSION_ID,
            'name' => 'Updated Mansion',
            'company_id' => 999,
        ]);

        $controller->editMansion($request, $siteSyncService, $roomSynchronizer);

        $this->assertDatabaseHas('articles', [
            'building_id' => self::MANSION_ID,
            'company_id' => self::COMPANY_ID,
        ]);
    }

    public function test_change_library_reports_site_failure_and_rolls_back_the_local_value(): void
    {
        $this->insertRoom([
            'building_id' => self::MANSION_ID,
            'mansion_id' => null,
            'library' => 0,
        ]);
        Auth::shouldReceive('check')->andReturn(false);
        Auth::shouldReceive('user')->once()->andReturn((object) ['company_id' => self::COMPANY_ID]);
        $apiService = Mockery::mock(CompanyApiService::class);
        $apiService->shouldReceive('editMansion')
            ->once()
            ->andReturn(SiteSyncResult::failure(500, 'Site synchronization failed.'));
        app()->instance(CompanyApiService::class, $apiService);
        $request = Request::create('/admin/ajax/change-library', 'POST', [
            'building_id' => self::MANSION_ID,
            'library' => 1,
        ], [], [], ['HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest']);

        $response = (new AjaxController)->changeLibrary($request);

        $this->assertFalse($response->getData(true)['status']);
        $this->assertDatabaseHas('articles', [
            'building_id' => self::MANSION_ID,
            'library' => 0,
        ]);
    }

    private function insertRoom(array $attributes): void
    {
        DB::table('articles')->insert(array_merge([
            'company_id' => self::COMPANY_ID,
            'building_id' => 9999,
            'mansion_id' => self::MANSION_ID,
            'del' => null,
            'zip' => 'original-zip',
            'name_apartment' => 'Original Mansion',
            'property_sub' => 'room-only',
            'property' => '1',
            'name' => 'Room',
            'memo1' => 'memo 1',
            'memo2' => 'memo 2',
            'build_note' => 'build note',
            'comment' => 'comment',
            'created_at' => now(),
            'updated_at' => now(),
        ], $attributes));
    }
}
