<?php

namespace Tests\Feature\Assets\Api;

use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\Location;
use App\Models\Statuslabel;
use App\Models\Supplier;
use App\Models\User;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Storage;
use Illuminate\Testing\Fluent\AssertableJson;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;

class StoreAssetTest extends TestCase
{
    public function test_requires_permission_to_create_asset()
    {
        $this->actingAsForApi(User::factory()->create())
            ->postJson(route('api.assets.store'))
            ->assertForbidden();
    }

    public function test_all_asset_attributes_are_stored()
    {
        $company = Company::factory()->create();
        $location = Location::factory()->create();
        $model = AssetModel::factory()->create();
        $rtdLocation = Location::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $supplier = Supplier::factory()->create();
        $user = User::factory()->createAssets()->create();
        $userAssigned = User::factory()->create();

        $response = $this->actingAsForApi($user)
            ->postJson(route('api.assets.store'), [
                'asset_eol_date' => '2024-06-02',
                'asset_tag' => 'random_string',
                'assigned_user' => $userAssigned->id,
                'company_id' => $company->id,
                'last_audit_date' => '2023-09-03',
                'location_id' => $location->id,
                'model_id' => $model->id,
                'name' => 'A New Asset',
                'notes' => 'Some notes',
                'order_number' => '5678',
                'purchase_cost' => '123.45',
                'purchase_date' => '2023-09-02',
                'requestable' => true,
                'rtd_location_id' => $rtdLocation->id,
                'serial' => '1234567890',
                'status_id' => $status->id,
                'supplier_id' => $supplier->id,
                'warranty_months' => 10,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);

        $this->assertTrue($asset->adminuser->is($user));

        $this->assertEquals('2024-06-02', $asset->asset_eol_date);
        $this->assertEquals('random_string', $asset->asset_tag);
        $this->assertEquals($userAssigned->id, $asset->assigned_to);
        $this->assertTrue($asset->company->is($company));
        $this->assertEquals('2023-09-03 00:00:00', $asset->last_audit_date);
        $this->assertTrue($asset->location->is($location));
        $this->assertTrue($asset->model->is($model));
        $this->assertEquals('A New Asset', $asset->name);
        $this->assertEquals('Some notes', $asset->notes);
        $this->assertEquals('5678', $asset->order_number);
        $this->assertEquals('123.45', $asset->purchase_cost);
        $this->assertTrue($asset->purchase_date->is('2023-09-02'));
        $this->assertEquals('1', $asset->requestable);
        $this->assertTrue($asset->defaultLoc->is($rtdLocation));
        $this->assertEquals('1234567890', $asset->serial);
        $this->assertTrue($asset->status->is($status));
        $this->assertTrue($asset->supplier->is($supplier));
        $this->assertEquals(10, $asset->warranty_months);

        $this->assertHasTheseActionLogs($asset, ['create', 'checkout']);
    }

    public function test_sets_last_audit_date_to_midnight_of_provided_date()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'last_audit_date' => '2023-09-03',
                'asset_tag' => '1234',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response['payload']['id']);
        $this->assertEquals('2023-09-03 00:00:00', $asset->last_audit_date);
    }

    public function test_last_audit_date_can_be_null()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                // 'last_audit_date' => '2023-09-03 12:23:45',
                'asset_tag' => '1234',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response['payload']['id']);
        $this->assertNull($asset->last_audit_date);
    }

    public function test_non_date_used_for_last_audit_date_returns_validation_error()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'last_audit_date' => 'this-is-not-valid',
                'asset_tag' => '1234',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('error');

        $this->assertNotNull($response->json('messages.last_audit_date'));
    }

    public function test_save_with_archived_status_and_user_returns_validation_error()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'assigned_to' => '1',
                'assigned_type' => User::class,
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->archived()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('error');

        $this->assertNotNull($response->json('messages.status_id'));
    }

    public function test_save_with_pending_status_and_user_returns_validation_error()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'assigned_to' => '1',
                'assigned_type' => User::class,
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->pending()->create()->id,
            ])
            ->assertOk()
            ->assertJson([
                'messages' => ['status_id' => [trans('admin/hardware/form.asset_not_deployable')]],
            ]);

        $this->assertNotNull($response->json('messages.status_id'));
    }

    public function test_raw_assigned_to_pair_is_ignored_on_store()
    {
        // Security regression: sending assigned_to + assigned_type on create must
        // not bypass checkOut() — the asset must be created unassigned with only a
        // 'create' log entry. Use assigned_user / assigned_asset / assigned_location
        // instead (those go through the proper checkout workflow).
        $target = User::factory()->create();
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '1235',
                'assigned_to' => $target->id,
                'assigned_type' => User::class,
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response->json()['payload']['id']);
        $this->assertNull($asset->assigned_to, 'assigned_to must not be set via the raw pair');
        $this->assertHasTheseActionLogs($asset, ['create']);
        $this->assertDatabaseMissing('action_logs', [
            'item_type' => Asset::class,
            'item_id' => $asset->id,
            'action_type' => 'checkout',
        ]);
    }

    public function test_raw_assigned_to_without_assigned_type_is_ignored_on_store()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '1235',
                'assigned_to' => '1',
                // 'assigned_type' => User::class — deliberately omit
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response->json()['payload']['id']);
        $this->assertNull($asset->assigned_to);
    }

    public function test_raw_assigned_to_with_bad_assigned_type_is_ignored_on_store()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '1235',
                'assigned_to' => '1',
                'assigned_type' => 'nonsense_string',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response->json()['payload']['id']);
        $this->assertNull($asset->assigned_to);
    }

    public function test_raw_assigned_type_without_assigned_to_is_ignored_on_store()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '1235',
                // 'assigned_to' => '1' — deliberately omit
                'assigned_type' => User::class,
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response->json()['payload']['id']);
        $this->assertNull($asset->assigned_to);
    }

    public function test_save_with_pending_status_without_user_is_successful()
    {
        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '1234',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->pending()->create()->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');
    }

    public function test_archived_depreciate_and_physical_can_be_null()
    {
        $model = AssetModel::factory()->ipadModel()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'archive' => null,
                'depreciate' => null,
                'physical' => null,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);
        $this->assertEquals(0, $asset->archived);
        $this->assertEquals(1, $asset->physical);
        $this->assertEquals(0, $asset->depreciate);
    }

    public function test_archived_depreciate_and_physical_can_be_empty()
    {
        $model = AssetModel::factory()->ipadModel()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'archive' => '',
                'depreciate' => '',
                'physical' => '',
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);
        $this->assertEquals(0, $asset->archived);
        $this->assertEquals(1, $asset->physical);
        $this->assertEquals(0, $asset->depreciate);
    }

    public function test_asset_eol_date_is_calculated_if_purchase_date_set()
    {
        $model = AssetModel::factory()->mbp13Model()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'purchase_date' => '2021-01-01',
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);
        $this->assertEquals('2024-01-01', $asset->asset_eol_date);
    }

    public function test_asset_eol_date_is_not_calculated_if_purchase_date_not_set()
    {
        $model = AssetModel::factory()->mbp13Model()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);
        $this->assertNull($asset->asset_eol_date);
    }

    public function test_asset_eol_explicit_is_set_if_asset_eol_date_is_explicitly_set()
    {
        $model = AssetModel::factory()->mbp13Model()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'asset_eol_date' => '2025-01-01',
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);
        $this->assertEquals('2025-01-01', $asset->asset_eol_date);
        $this->assertTrue($asset->eol_explicit);
    }

    public function test_asset_gets_asset_tag_with_auto_increment()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);
        $this->assertNotNull($asset->asset_tag);
    }

    public function test_asset_creation_fails_with_no_asset_tag_or_auto_increment()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();

        $this->settings->disableAutoIncrement();

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('error');
    }

    public function test_stores_period_as_decimal_separator_for_purchase_cost()
    {
        $this->settings->set([
            'default_currency' => 'USD',
            'digit_separator' => '1,234.56',
        ]);

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => 'random-string',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
                // API accepts float
                'purchase_cost' => 12.34,
            ])
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response['payload']['id']);

        $this->assertEquals(12.34, $asset->purchase_cost);
    }

    public function test_stores_period_as_comma_separator_for_purchase_cost()
    {
        $this->settings->set([
            'default_currency' => 'EUR',
            'digit_separator' => '1.234,56',
        ]);

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => 'random-string',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
                // API also accepts string for comma separated values
                'purchase_cost' => '12,34',
            ])
            ->assertStatusMessageIs('success');

        $asset = Asset::find($response['payload']['id']);

        $this->assertEquals(12.34, $asset->purchase_cost);
    }

    public function test_unique_serial_numbers_is_enforced_when_enabled()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $serial = '1234567890';

        $this->settings->enableAutoIncrement();
        $this->settings->enableUniqueSerialNumbers();

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'serial' => $serial,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'serial' => $serial,
            ])
            ->assertOk()
            ->assertStatusMessageIs('error');
    }

    public function test_unique_serial_numbers_is_not_enforced_when_disabled()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $serial = '1234567890';

        $this->settings->enableAutoIncrement();
        $this->settings->disableUniqueSerialNumbers();

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'serial' => $serial,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'serial' => $serial,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');
    }

    public function test_asset_tags_must_be_unique_when_undeleted()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $asset_tag = '1234567890';

        $this->settings->disableAutoIncrement();

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => $asset_tag,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => $asset_tag,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('error');
    }

    public function test_asset_tags_can_be_duplicated_if_deleted()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $asset_tag = '1234567890';

        $this->settings->disableAutoIncrement();

        $response = $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => $asset_tag,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        Asset::find($response['payload']['id'])->delete();

        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => $asset_tag,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success');
    }

    public function test_an_asset_can_be_checked_out_to_user_on_store()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $user = User::factory()->createAssets()->create();
        $userAssigned = User::factory()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi($user)
            ->postJson(route('api.assets.store'), [
                'assigned_user' => $userAssigned->id,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);

        $this->assertTrue($asset->adminuser->is($user));
        $this->assertTrue($asset->checkedOutToUser());
        $this->assertTrue($asset->assignedTo->is($userAssigned));
        $this->assertHasTheseActionLogs($asset, ['create', 'checkout']);
    }

    public function test_store_rejects_cross_company_checkout_target_with_full_company_support_enabled()
    {
        $this->settings->enableMultipleFullCompanySupport();

        [$companyA, $companyB] = Company::factory()->count(2)->create();

        $actorInCompanyA = User::factory()->createAssets()->forCompany($companyA)->create();
        $targetUserInCompanyB = User::factory()->forCompany($companyB)->create();

        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $assetTag = 'fmcs-store-rollback-asset';

        $this->actingAsForApi($actorInCompanyA)
            ->postJson(route('api.assets.store'), [
                'asset_tag' => $assetTag,
                'model_id' => $model->id,
                'status_id' => $status->id,
                'company_id' => $companyA->id,
                'assigned_user' => $targetUserInCompanyB->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('error')
            ->assertMessagesAre(trans('general.error_user_company'));

        $this->assertDatabaseMissing('assets', [
            'asset_tag' => $assetTag,
        ]);

        $this->assertDatabaseMissing('action_logs', [
            'action_type' => 'checkout',
            'target_type' => User::class,
            'target_id' => $targetUserInCompanyB->id,
            'item_type' => Asset::class,
        ]);
    }

    public static function checkoutTargets()
    {
        yield 'Users' => [
            function () {
                return [
                    'key' => 'assigned_user',
                    'value' => [
                        User::factory()->create()->id,
                        User::factory()->create()->id,
                    ],
                ];
            },
        ];

        yield 'Locations' => [
            function () {
                return [
                    'key' => 'assigned_location',
                    'value' => [
                        Location::factory()->create()->id,
                        Location::factory()->create()->id,
                    ],
                ];
            },
        ];

        yield 'Assets' => [
            function () {
                return [
                    'key' => 'assigned_asset',
                    'value' => [
                        Asset::factory()->create()->id,
                        Asset::factory()->create()->id,
                    ],
                ];
            },
        ];
    }

    /** @link https://app.shortcut.com/grokability/story/29181 */
    #[DataProvider('checkoutTargets')]
    public function test_assigned_field_validation_cannot_be_array($data)
    {
        ['key' => $key, 'value' => $value] = $data();

        $this->actingAsForApi(User::factory()->createAssets()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '123456',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
                $key => $value,
            ])
            ->assertStatusMessageIs('error')
            ->assertJson(function (AssertableJson $json) use ($key) {
                $json->has("messages.{$key}")->etc();
            });
    }

    public function test_an_asset_can_be_checked_out_to_location_on_store()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $location = Location::factory()->create();
        $user = User::factory()->createAssets()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi($user)
            ->postJson(route('api.assets.store'), [
                'assigned_location' => $location->id,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $asset = Asset::find($response['payload']['id']);

        $this->assertTrue($asset->adminuser->is($user));
        $this->assertTrue($asset->checkedOutToLocation());
        $this->assertTrue($asset->location->is($location));
        $this->assertHasTheseActionLogs($asset, ['create', 'checkout']);
    }

    public function test_an_asset_can_be_checked_out_to_asset_on_store()
    {
        $model = AssetModel::factory()->create();
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $asset = Asset::factory()->create();
        $user = User::factory()->createAssets()->create();

        $this->settings->enableAutoIncrement();

        $response = $this->actingAsForApi($user)
            ->postJson(route('api.assets.store'), [
                'assigned_asset' => $asset->id,
                'model_id' => $model->id,
                'status_id' => $status->id,
            ])
            ->assertOk()
            ->assertStatusMessageIs('success')
            ->json();

        $apiAsset = Asset::find($response['payload']['id']);

        $this->assertTrue($apiAsset->adminuser->is($user));
        $this->assertTrue($apiAsset->checkedOutToAsset());
        // I think this makes sense, but open to a sanity check
        $this->assertTrue($asset->assignedAssets()->find($response['payload']['id'])->is($apiAsset));
        $this->assertHasTheseActionLogs($asset, ['create'/* , 'checkout' */]); // TODO - should be the two events
    }

    /**
     * @link https://app.shortcut.com/grokability/story/24475
     */
    public function test_company_id_needs_to_be_integer()
    {
        $this->actingAsForApi(User::factory()->createAssets()->create())
            ->postJson(route('api.assets.store'), [
                'company_id' => [1],
            ])
            ->assertStatusMessageIs('error')
            ->assertJson(function (AssertableJson $json) {
                $json->has('messages.company_id')->etc();
            });
    }

    public function test_serial_validation()
    {
        $this->actingAsForApi(User::factory()->superuser()->create())
            ->postJson(route('api.assets.store'), [
                'asset_tag' => '1234',
                'model_id' => AssetModel::factory()->create()->id,
                'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
                'serial' => [
                    // this should not be an array
                ],
            ])
            ->assertOk()
            ->assertStatusMessageIs('error')
            ->assertMessagesContains('serial');
    }

    public function test_encrypted_custom_field_can_be_stored()
    {
        $this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');

        $status = Statuslabel::factory()->readyToDeploy()->create();
        $field = CustomField::factory()->testEncrypted()->create();
        $superuser = User::factory()->superuser()->create();
        $assetData = Asset::factory()->hasEncryptedCustomField($field)->make();

        $response = $this->actingAsForApi($superuser)
            ->postJson(route('api.assets.store'), [
                $field->db_column_name() => 'This is encrypted field',
                'model_id' => $assetData->model->id,
                'status_id' => $status->id,
                'asset_tag' => '1234',
            ])
            ->assertStatusMessageIs('success')
            ->assertOk()
            ->json();

        $asset = Asset::findOrFail($response['payload']['id']);
        $this->assertEquals('This is encrypted field', Crypt::decrypt($asset->{$field->db_column_name()}));
    }

    public function test_encrypted_custom_field_validation_passes()
    {
        $this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');

        $status = Statuslabel::factory()->readyToDeploy()->create();
        $alphaField = CustomField::factory()->encrypt()->alpha()->create();
        $numericField = CustomField::factory()->encrypt()->numeric()->create();
        $emailField = CustomField::factory()->encrypt()->email()->create();
        $fields = [$alphaField, $numericField, $emailField];
        $superuser = User::factory()->superuser()->create();
        $assetData = Asset::factory()->hasMultipleCustomFields($fields)->make();

        $response = $this->actingAsForApi($superuser)
            ->postJson(route('api.assets.store'), [
                $alphaField->db_column_name() => 'Thisisencryptedfield',
                $numericField->db_column_name() => '1234567890',
                $emailField->db_column_name() => 'poop@poop.com',
                'model_id' => $assetData->model->id,
                'status_id' => $status->id,
                'asset_tag' => '1234',
            ])
            ->assertStatusMessageIs('success')
            ->assertOk()
            ->json();

        $asset = Asset::findOrFail($response['payload']['id']);
        $this->assertEquals('Thisisencryptedfield', Crypt::decrypt($asset->{$alphaField->db_column_name()}));
        $this->assertEquals('1234567890', Crypt::decrypt($asset->{$numericField->db_column_name()}));
        $this->assertEquals('poop@poop.com', Crypt::decrypt($asset->{$emailField->db_column_name()}));
    }

    public function test_encrypted_custom_field_validation_fails()
    {
        $this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');

        $status = Statuslabel::factory()->readyToDeploy()->create();
        $alphaField = CustomField::factory()->encrypt()->alpha()->create();
        $numericField = CustomField::factory()->encrypt()->numeric()->create();
        $emailField = CustomField::factory()->encrypt()->email()->create();
        $fields = [$alphaField, $numericField, $emailField];
        $superuser = User::factory()->superuser()->create();
        $assetData = Asset::factory()->hasMultipleCustomFields($fields)->make();
        $cleaned_name = trim(preg_replace('/_+|snipeit|\d+/', ' ', $alphaField->db_column_name()));

        $response = $this->actingAsForApi($superuser)
            ->postJson(route('api.assets.store'), [
                $alphaField->db_column_name() => 'Thisisencryptedfield123',
                'model_id' => $assetData->model->id,
                'status_id' => $status->id,
                'asset_tag' => '1234',
            ])
            ->assertStatusMessageIs('error')
            ->assertJsonPath('messages.'.$alphaField->db_column_name(), [trans('validation.alpha', ['attribute' => $cleaned_name])])
            ->assertOk()
            ->json();
    }

    public function test_permission_needed_to_store_encrypted_field()
    {
        // @todo:
        $this->markTestIncomplete();

        $status = Statuslabel::factory()->readyToDeploy()->create();
        $field = CustomField::factory()->testEncrypted()->create();
        $normal_user = User::factory()->editAssets()->create();
        $assetData = Asset::factory()->hasEncryptedCustomField($field)->make();

        $response = $this->actingAsForApi($normal_user)
            ->postJson(route('api.assets.store'), [
                $field->db_column_name() => 'Some Other Value Entirely!',
                'model_id' => $assetData->model->id,
                'status_id' => $status->id,
                'asset_tag' => '1234',
            ])
            // @todo: this is 403 unauthorized
            ->assertStatusMessageIs('success')
            ->assertOk()
            ->assertMessagesAre('Asset updated successfully, but encrypted custom fields were not due to permissions')
            ->json();

        $asset = Asset::findOrFail($response['payload']['id']);
        $this->assertEquals('This is encrypted field', Crypt::decrypt($asset->{$field->db_column_name()}));
    }

    public function test_base64_asset_images()
    {
        $status = Statuslabel::factory()->readyToDeploy()->create();
        $model = AssetModel::factory()->create();
        $superuser = User::factory()->superuser()->create();

        $response = $this->actingAsForApi($superuser)
            ->postJson(route('api.assets.store'), [
                'model_id' => $model->id,
                'status_id' => $status->id,
                'asset_tag' => '1234',
                'image' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAEsAQMAAADXeXeBAAAABlBMVEX+AAD///+KQee0AAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH5QQbCAoNcoiTQAAAACZJREFUaN7twTEBAAAAwqD1T20JT6AAAAAAAAAAAAAAAAAAAICnATvEAAEnf54JAAAAAElFTkSuQmCC',
            ])
            ->assertStatusMessageIs('success')
            ->assertOk()
            ->json();

        $asset = Asset::findOrFail($response['payload']['id']);
        $this->assertEquals($asset->asset_tag, '1234');
        $image_data = Storage::disk('public')->get(app('assets_upload_path').e($asset->image));
        // $this->assertEquals('3d67fb99a0b6926e350f7b71397525d7a6b936c1', sha1($image_data)); //this doesn't work because the image gets resized - use the resized hash instead
        $this->assertEquals('db2e13ba04318c99058ca429d67777322f48566b', sha1($image_data));
    }
}
