<?php

namespace Cknow\Money\Tests;

use Cknow\Money\Money;
use Cknow\Money\Tests\Database\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
use Money\Exception\ParserException;
use Money\Money as BaseMoney;
use stdClass;

/**
 * The money cast test.
 */
class MoneyCastTest extends TestCase
{
    use RefreshDatabase;

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

        Money::setCurrencies(config('money.currencies'));
    }

    protected function defineDatabaseMigrations(): void
    {
        $this->loadMigrationsFrom(__DIR__.'/Database/Migrations');
    }

    public function test_casts_money_when_retrieving_casted_values()
    {
        DB::table('users')->insert([
            'money' => '1234.56',
            'wage' => 50000,
            'debits' => null,
            'credits' => 12.00,
            'currency' => 'AUD',
        ]);

        $user = User::findOrFail(1);

        static::assertInstanceOf(Money::class, $user->money);
        static::assertInstanceOf(Money::class, $user->wage);
        static::assertNull($user->debits);
        static::assertInstanceOf(Money::class, $user->credits);

        static::assertSame('123456', $user->money->getAmount());
        static::assertSame('USD', $user->money->getCurrency()->getCode());

        static::assertSame('50000', $user->wage->getAmount());
        static::assertSame('EUR', $user->wage->getCurrency()->getCode());

        $user->debits = 100.99;
        $user->credits = '$99';

        static::assertSame('10099', $user->debits->getAmount());
        static::assertSame('AUD', $user->debits->getCurrency()->getCode());

        static::assertSame('9900', $user->credits->getAmount());
        static::assertSame('USD', $user->credits->getCurrency()->getCode());

        $user->save();

        static::assertSame(1, $user->id);

        $this->assertDatabaseHas('users', [
            'id' => 1,
            'money' => '$1,234.56',
            'wage' => 50000,
            'debits' => 100.99,
            'credits' => 99,
            'currency' => 'AUD',
        ]);
    }

    public function test_casts_money_when_setting_casted_values()
    {
        $user = new User([
            'money' => 0,
            'wage' => '65000.00',
            'debits' => null,
            'currency' => 'CAD',
        ]);

        static::assertSame('0', $user->money->getAmount());
        static::assertSame('USD', $user->money->getCurrency()->getCode());

        static::assertSame('6500000', $user->wage->getAmount());
        static::assertSame('EUR', $user->wage->getCurrency()->getCode());

        static::assertNull($user->debits);

        $user->money = new BaseMoney(10000, $user->money->getCurrency());

        static::assertSame('10000', $user->money->getAmount());

        $user->money = 100;
        $user->wage = 70500.19;
        $user->debits = '¥213860';

        static::assertSame('100', $user->money->getAmount());
        static::assertSame('USD', $user->money->getCurrency()->getCode());

        static::assertSame('7050019', $user->wage->getAmount());
        static::assertSame('EUR', $user->wage->getCurrency()->getCode());

        static::assertSame('213860', $user->debits->getAmount());
        static::assertSame('JPY', $user->debits->getCurrency()->getCode());
        static::assertSame('JPY', $user->currency);

        $user->money = '100,000.22';
        $user->debits = 'Ƀ0.00012345';

        static::assertSame('10000022', $user->money->getAmount());
        static::assertSame('USD', $user->money->getCurrency()->getCode());

        static::assertSame('12345', $user->debits->getAmount());
        static::assertSame('XBT', $user->debits->getCurrency()->getCode());
        static::assertSame('XBT', $user->currency);

        $user->save();

        static::assertSame(1, $user->id);

        $this->assertDatabaseHas('users', [
            'id' => 1,
            'money' => '$100,000.22',
            'wage' => 7050019,
            'debits' => 0.00012345,
            'currency' => 'XBT',
        ]);
    }

    public function test_fails_to_set_invalid_money()
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Invalid data provided for Cknow\Money\Tests\Database\Models\User::$money');

        new User(['money' => new stdClass]);
    }

    public function test_fails_to_parse_invalid_money()
    {
        $this->expectException(ParserException::class);
        $this->expectExceptionMessage('Unable to parse abc');

        new User(['money' => 'abc']);
    }

    public function test_set_currency_for_new_empty_user()
    {
        $user = new User;

        $user->debits = 100.99;

        static::assertSame('10099', $user->debits->getAmount());
        static::assertSame('USD', $user->debits->getCurrency()->getCode());
        static::assertSame('USD', $user->currency);
    }
}
