<?php

namespace Tests\Feature;

use App\BillableType;
use App\Master\InvoiceGenerator;
use App\Mail\InvoiceNotification;
use App\Tier;
use Database\Seeders\Global\PrimaryBillableSeed;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Mtc\MercuryDataModels\Billable;
use Mtc\MercuryDataModels\Invoice;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\TenantBillingDetail;
use Tests\TestCase;

class InvoiceTest extends TestCase
{
    use RefreshDatabase;

    protected bool $tenancy_make_databases = false;

    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testGenerateSimpleInvoice()
    {
        $this->seed(PrimaryBillableSeed::class);
        $tenant = Tenant::factory()->create(['tier' => 'lite']);
        $lite_tier_billable = Billable::query()->where('code', 'lite')->firstOrFail();
        TenantBillingDetail::factory()->create([
            'tenant_id' => $tenant->id,
        ]);

        $tenant->billables()
            ->create([
                'billable_id' => $lite_tier_billable->id,
                'price' => 123,
                'quantity' => 1,
            ]);

        Mail::fake();
        $invoice = (new InvoiceGenerator())->create($tenant);

        $this->assertInstanceOf(Invoice::class, $invoice);
        $this->assertEquals($tenant->id, $invoice->tenant_id);
        $this->assertEquals(123, $invoice->amount);

        Mail::assertQueued(InvoiceNotification::class);
    }

    public function testGenerateInvoiceWithMultipleItems()
    {
        $this->seed(PrimaryBillableSeed::class);
        $tenant = Tenant::factory()->create(['tier' => 'pro']);
        $tier_billable = Billable::query()->where('code', 'pro')->firstOrFail();
        $plugin_feature = Billable::factory()->create(['type' => BillableType::PLUGIN->value]);
        TenantBillingDetail::factory()->create([
            'tenant_id' => $tenant->id,
        ]);

        $tier_tenant_billable = $tenant->billables()
            ->create([
                'billable_id' => $tier_billable->id,
                'price' => 123,
                'quantity' => 1,
            ]);
        $plugin_tenant_billable = $tenant->billables()
            ->create([
                'billable_id' => $plugin_feature->id,
                'price' => 100,
                'quantity' => 1,
            ]);

        Mail::fake();
        $tenant->load([
            'billables.billable',
            'billingDetails',
        ]);
        $invoice = (new InvoiceGenerator())->create($tenant);

        $this->assertInstanceOf(Invoice::class, $invoice);
        $this->assertEquals($tenant->id, $invoice->tenant_id);
        $this->assertEquals(223, $invoice->amount);
        $this->assertEquals(2, $invoice->items()->count());
        $this->assertTrue($invoice->items()->where('billable_id', $tier_tenant_billable->id)->exists());
        $this->assertTrue($invoice->items()->where('billable_id', $plugin_tenant_billable->id)->exists());

        Mail::assertQueued(InvoiceNotification::class);
    }

    public function testUpgradePlanInvoice()
    {
        $this->seed(PrimaryBillableSeed::class);
        $tenant = Tenant::factory()->create(['tier' => 'lite']);
        $lite_tier_billable = Billable::query()->where('code', 'lite')->firstOrFail();
        TenantBillingDetail::factory()->create([
            'tenant_id' => $tenant->id,
        ]);

        $tenant->billables()
            ->create([
                'billable_id' => $lite_tier_billable->id,
                'price' => 123,
                'quantity' => 1,
            ]);

        $this->assertFalse($tenant->invoices()->exists());
        Mail::fake();
        $tenant->changeTier('pro');

        $this->assertEquals('pro', $tenant->tier);
        $this->assertEquals('lite', $tenant->previous_tier);
        $this->assertEquals(1, $tenant->invoices()->count());

    }

    public function testDowngradePlanHasNoInvoice()
    {
        $this->seed(PrimaryBillableSeed::class);
        $tenant = Tenant::factory()->create(['tier' => 'pro']);
        $pro_tier_billable = Billable::query()->where('code', 'pro')->firstOrFail();
        TenantBillingDetail::factory()->create([
            'tenant_id' => $tenant->id,
        ]);

        $tenant->billables()
            ->create([
                'billable_id' => $pro_tier_billable->id,
                'price' => 450,
                'quantity' => 1,
            ]);

        Mail::fake();
        $tenant->changeTier('lite');

        $this->assertEquals('pro', $tenant->tier);
        $this->assertEquals('lite', $tenant->change_tier_on_next_invoice_to);

        Mail::assertNothingSent();
    }

    /**
     * @runInSeparateProcess
     * @preserveGlobalState disabled
     */
    public function testDowngradeRemovesAdvancedFeaturesWhenCharging()
    {
        \Mockery::mock('alias:Mtc\MercuryDataModels\Setting')
            ->shouldReceive('query->whereIn->where->where->update')
            ->andReturnNull();

        $this->seed(PrimaryBillableSeed::class);
        $tenant = Tenant::factory()->create(['tier' => 'pro']);

        $tenant->update(['tier' => Tier::ENTERPRISE->value]);

        $tier_billable = Billable::factory()->create(['code' => Tier::ENTERPRISE->value, 'type' => 'tier']);
        $plugin_feature = Billable::factory()->create(['type' => BillableType::PLUGIN->value]);
        $addon_feature = Billable::factory()->create(['type' => BillableType::ADDON->value]);
        $theme_feature = Billable::factory()->create(['type' => BillableType::THEME->value]);
        $custom_feature = Billable::factory()->create(['type' => BillableType::CUSTOM->value]);
        TenantBillingDetail::factory()->create([
            'tenant_id' => $tenant->id,
        ]);

        $tenant->billables()
            ->create([
                'billable_id' => $tier_billable->id,
                'price' => 450,
                'quantity' => 1,
            ]);

        $tenant->billables()
            ->create([
                'billable_id' => $plugin_feature->id,
                'price' => 450,
                'quantity' => 1,
            ]);

        $tenant->billables()
            ->create([
                'billable_id' => $addon_feature->id,
                'price' => 450,
                'quantity' => 1,
            ]);

        $tenant->billables()
            ->create([
                'billable_id' => $theme_feature->id,
                'price' => 450,
                'quantity' => 1,
            ]);

        $tenant->billables()
            ->create([
                'billable_id' => $custom_feature->id,
                'price' => 450,
                'quantity' => 1,
            ]);

        $this->assertTrue($tenant->billables()->where('billable_id', $plugin_feature->id)->exists());
        $this->assertTrue($tenant->billables()->where('billable_id', $addon_feature->id)->exists());
        $this->assertTrue($tenant->billables()->where('billable_id', $theme_feature->id)->exists());
        $this->assertTrue($tenant->billables()->where('billable_id', $custom_feature->id)->exists());

        Mail::fake();

        $tenant->changeTier(Tier::PRO->value);
        $tenant->checkBillingChange();
        $this->assertTrue($tenant->billables()->where('billable_id', $plugin_feature->id)->exists());
        $this->assertTrue($tenant->billables()->where('billable_id', $addon_feature->id)->exists());
        $this->assertFalse($tenant->billables()->where('billable_id', $theme_feature->id)->exists());
        $this->assertFalse($tenant->billables()->where('billable_id', $custom_feature->id)->exists());

        $tenant->changeTier(Tier::STANDARD->value);
        $tenant->checkBillingChange();
        $this->assertTrue($tenant->billables()->where('billable_id', $plugin_feature->id)->exists());
        $this->assertFalse($tenant->billables()->where('billable_id', $addon_feature->id)->exists());

        $tenant->changeTier(Tier::LITE->value);
        $tenant->checkBillingChange();
        $this->assertFalse($tenant->billables()->where('billable_id', $plugin_feature->id)->exists());
    }
}
