<?php

namespace Tests\Feature;

use App\Console\Commands\BackupCommand;
use App\Console\Commands\ClearAudit;
use App\Console\Commands\ExceptionReportingTest;
use App\Console\Commands\ExportToSilverBullet;
use App\Console\Commands\FixSpecDataMorphType;
use App\Console\Commands\InstallLocalCommand;
use App\Console\Commands\MakeUserAdmin;
use App\Console\Commands\RemoveArchivedVehicles;
use App\Console\Commands\SeedAdminUser;
use App\Console\Commands\SeedThemeData;
use App\Console\Commands\SetMissingVehicleTitles;
use App\Console\Commands\SyncVehicles;
use App\Console\Commands\TenantsSyncTaxonomies;
use App\Facades\Settings;
use App\Jobs\RunScheduledSyncTask;
use App\Jobs\RunSilverBulletExport;
use App\Master\Models\BodyStyleType;
use App\Master\Models\DrivetrainType;
use App\Master\Models\FuelType;
use App\Master\Models\TransmissionType;
use App\Master\Models\VehicleMake;
use App\Master\Models\VehicleModel;
use App\Models\Seed;
use App\Traits\TrackedSeeding;
use Carbon\Carbon;
use Database\Seeders\Tenant\BaseSettingSeeder;
use Database\Seeders\Tenant\CarWowExportSeeder;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Queue;
use Mtc\ContentManager\Models\ContentElement;
use Mtc\ContentManager\Models\Menu;
use Mtc\ContentManager\Models\Template;
use Mtc\MercuryDataModels\Audit;
use Mtc\MercuryDataModels\GlobalContent;
use Mtc\MercuryDataModels\Page;
use Mtc\MercuryDataModels\ReportingStat;
use Mtc\MercuryDataModels\Role;
use Mtc\MercuryDataModels\SalesChannelHistory;
use Mtc\MercuryDataModels\User;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleFeature;
use Mtc\MercuryDataModels\VehicleStandardEquipment;
use Mtc\MercuryDataModels\VehicleTechnicalData;
use Tests\TestCase;
use Tests\UserForTenant;

class CommandTest extends TestCase
{
    use DatabaseMigrations;
    use UserForTenant;
    use TrackedSeeding;

    protected $tenancy = true;

    public function testTrackedSeed()
    {
        $this->artisan('tenants:seed')->assertExitCode(0);
        $this->assertTrue(Seed::query()->where('seeder', CarWowExportSeeder::class)->exists());
    }

    public function testDeploy()
    {
        $this->artisan('deploy')->assertExitCode(0);
        $this->assertTrue(Seed::query()->where('seeder', CarWowExportSeeder::class)->exists());
    }

    public function testClearStats()
    {
        Config::set('automotive.dashboard-history-length', 10);
        ReportingStat::factory()->create(['date' => Carbon::now()->subDays(20)]);
        ReportingStat::factory()->create(['date' => Carbon::now()]);

        $this->assertEquals(2, ReportingStat::query()->count());
        $this->artisan('clear:stats')->assertExitCode(0);
        $this->assertEquals(1, ReportingStat::query()->count());
    }

    public function testClearSalesChannelHistory()
    {
        Config::set('logging.sales-channel-history-age', 10);
        SalesChannelHistory::factory()->create(['created_at' => Carbon::now()->subDays(20)]);
        SalesChannelHistory::factory()->create(['created_at' => Carbon::now()]);

        $this->assertEquals(2, SalesChannelHistory::query()->count());
        $this->artisan('clear:sales-channel-history')->assertExitCode(0);
        $this->assertEquals(1, SalesChannelHistory::query()->count());
    }

    public function testClearModelsWithoutMakes()
    {
        $make = VehicleMake::factory()->create();
        VehicleModel::factory()->create(['make_id' => $make->id]);
        VehicleModel::factory()->create(['make_id' => 0]);

        $this->assertEquals(2, VehicleModel::query()->count());
        $this->artisan('clean:vehicle-models')->assertExitCode(0);
        $this->assertEquals(1, VehicleModel::query()->count());
    }

    public function testCleanAudit()
    {
        Audit::query()->create([
            'user_type' => 'user',
            'user_id' => $this->getUser()->id,
            'auditable_type' => 'foo',
            'auditable_id' => 1,
            'old_values' => '[]',
            'new_values' => '[]',
            'event' => 'updated',
            'url' => 'foo.com',
            'ip_address' => '1.1.1.1',
            'user_agent' => 'abc',
            'created_at' => Carbon::now()->subYears(2),
        ]);
        Audit::query()->create([
            'user_type' => 'user',
            'user_id' => $this->getUser()->id,
            'auditable_type' => 'foo',
            'auditable_id' => 1,
            'event' => 'updated',
            'old_values' => '[]',
            'new_values' => '[]',
            'url' => 'foo.com',
            'ip_address' => '1.1.1.1',
            'user_agent' => 'abc',
            'created_at' => Carbon::now(),
        ]);
        $this->artisan(ClearAudit::class)->assertExitCode(0);
        $this->assertEquals(1, Audit::query()->count());
    }

    public function testExceptionThrowing()
    {
        $this->expectException(\Exception::class);
        $this->artisan(ExceptionReportingTest::class);
    }

    public function testFixSpecDataMorphType()
    {
        VehicleFeature::factory()->create(['vehicle_type' => null]);
        VehicleTechnicalData::factory()->create(['vehicle_type' => null]);
        VehicleStandardEquipment::factory()->create(['vehicle_type' => null]);
        $this->artisan(FixSpecDataMorphType::class)->assertExitCode(0);


        $this->assertFalse(VehicleFeature::query()->where(['vehicle_type' => null])->exists());
        $this->assertFalse(VehicleTechnicalData::query()->where(['vehicle_type' => null])->exists());
        $this->assertFalse(VehicleStandardEquipment::query()->where(['vehicle_type' => null])->exists());
    }

    public function testMakeUserAdmin()
    {
        Role::query()->create(['name' => 'mtc']);
        $user = User::factory()->create(['email' => 'foo@baz.com']);
        $this->artisan(MakeUserAdmin::class, ['email' => 'foo@baz.com'])
            ->assertExitCode(0);

        $user->hasRole('mtc');
    }

    public function testSeedAdminUser()
    {
        Role::query()->create(['name' => 'mtc']);
        $this->artisan(SeedAdminUser::class)
            ->expectsQuestion('Email:', 'foo@baz.com')
            ->expectsQuestion('Password: ', 'test123')
            ->assertExitCode(0);

        $this->assertTrue(User::query()->where('email', 'foo@baz.com')->exists());
        $user = User::query()->where('email', 'foo@baz.com')->first();
        $user->hasRole('mtc');
    }

    public function testSeedTheme()
    {
        $this->seed(BaseSettingSeeder::class);
        $this->artisan(SeedThemeData::class)->assertExitCode(0);
        $this->assertTrue(ContentElement::query()->exists());
        $this->assertTrue(GlobalContent::query()->exists());
        $this->assertTrue(Template::query()->exists());
        $this->assertTrue(Page::query()->exists());
        $this->assertTrue(Menu::query()->exists());
    }

    public function testExportToSilverBullet()
    {
        Queue::fake();
        $this->artisan(ExportToSilverBullet::class)->assertExitCode(0);
        Queue::assertPushed(RunSilverBulletExport::class);
    }

    public function testTenantTaxonomySync()
    {
        VehicleMake::factory(10)->create();
        VehicleModel::factory(10)->create();
        FuelType::factory(10)->create();
        DrivetrainType::factory(10)->create();
        TransmissionType::factory(10)->create();
        BodyStyleType::factory(10)->create();
        $this->artisan(TenantsSyncTaxonomies::class)->assertExitCode(0);
    }

    public function testPurgeVehicles()
    {
        tenant()->live_at = Carbon::now();
        Vehicle::factory(5)->create(['deleted_at' => Carbon::now()->subMonths(6), 'purged_at' => null]);
        Vehicle::factory(4)->create(['deleted_at' => Carbon::now()->subWeek(), 'purged_at' => null]);
        Vehicle::factory(2)->create();
        Vehicle::factory(3)->create(['deleted_at' => Carbon::now()->subYears(3), 'purged_at' => null]);
        $this->artisan(RemoveArchivedVehicles::class)->assertExitCode(0);
        $this->assertEquals(11, Vehicle::withTrashed()->count());
        $this->assertEquals(5, Vehicle::onlyTrashed()->whereNotNull('purged_at')->count());
        $this->assertEquals(4, Vehicle::onlyTrashed()->whereNull('purged_at')->count());
        $this->assertEquals(0, Vehicle::onlyTrashed()
            ->where('deleted_at', '<=', Carbon::now()->subYears(3))
            ->count()
        );
        $this->assertEquals(2, Vehicle::query()->count());
    }

    public function testSetMissingVehicleTitlesDisabled()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Automatically set vehicle title from make and model',
            'config_key' => 'auto-update-vehicle-title',
            'type' => 'boolean',
            'value' => false,
        ]);

        Vehicle::factory()->create([
            'title' => null,
        ]);

        $this->artisan(SetMissingVehicleTitles::class)->assertExitCode(0);

        // assert that we didn't set the title
        $this->assertCount(1, Vehicle::query()->whereNull('title')->get());
    }

    public function testSetMissingVehicleTitlesNoEligibleVehicles()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Automatically set vehicle title from make and model',
            'config_key' => 'auto-update-vehicle-title',
            'type' => 'boolean',
            'value' => false,
        ]);

        Vehicle::factory()->create([
            'title' => null,
            'make_id' => null,
            'model_id' => null,
        ]);

        Settings::update('auto-update-vehicle-title', true);

        $this->artisan(SetMissingVehicleTitles::class)->assertExitCode(0);

        // assert that we didn't set the title
        $this->assertCount(1, Vehicle::query()->whereNull('title')->get());
    }

    public function testSetMissingVehicleTitleSet()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Automatically set vehicle title from make and model',
            'config_key' => 'auto-update-vehicle-title',
            'type' => 'boolean',
            'value' => false,
        ]);

        $make = \Mtc\MercuryDataModels\VehicleMake::factory()->create([
            'name' => 'foo',
        ]);

        $model = \Mtc\MercuryDataModels\VehicleModel::factory()->create([
            'make_id' => $make->id,
            'name' => 'bar',
        ]);

        Vehicle::factory()->create([
            'title' => 'existing title',
            'make_id' => $make->id,
            'model_id' => $model->id,
        ]);

        Settings::update('auto-update-vehicle-title', true);

        $this->artisan(SetMissingVehicleTitles::class)->assertExitCode(0);

        // assert that we didn't overwrite the existing title
        $this->assertCount(1, Vehicle::query()->where('title', 'existing title')->get());
    }

    public function testSetMissingVehicleTitlesWorking()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Automatically set vehicle title from make and model',
            'config_key' => 'auto-update-vehicle-title',
            'type' => 'boolean',
            'value' => false,
        ]);

        $make = \Mtc\MercuryDataModels\VehicleMake::factory()->create([
            'name' => 'foo',
        ]);

        $model = \Mtc\MercuryDataModels\VehicleModel::factory()->create([
            'make_id' => $make->id,
            'name' => 'bar',
        ]);

        Vehicle::factory()->create([
            'title' => null,
            'make_id' => $make->id,
            'model_id' => $model->id,
        ]);

        Settings::update('auto-update-vehicle-title', true);

        $this->artisan(SetMissingVehicleTitles::class)->assertExitCode(0);

        // assert that we correctly set the title
        $this->assertCount(1, Vehicle::query()->where('title', 'foo bar')->get());
    }

    public function testSetMissingVehicleTitlesChunk()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Automatically set vehicle title from make and model',
            'config_key' => 'auto-update-vehicle-title',
            'type' => 'boolean',
            'value' => false,
        ]);

        $make = \Mtc\MercuryDataModels\VehicleMake::factory()->create([
            'name' => 'foo',
        ]);

        $model = \Mtc\MercuryDataModels\VehicleModel::factory()->create([
            'make_id' => $make->id,
            'name' => 'bar',
        ]);

        Vehicle::factory(250)->create([
            'title' => null,
            'make_id' => $make->id,
            'model_id' => $model->id,
        ]);

        Settings::update('auto-update-vehicle-title', true);

        $this->artisan(SetMissingVehicleTitles::class)->assertExitCode(0);

        // assert that we correctly set the title
        $this->assertCount(250, Vehicle::query()->where('title', 'foo bar')->get());
    }
}
