<?php

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
| Faker help: https://github.com/fzaninotto/Faker#formatters
*/

/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Entity;
use App\EntityClass;
use App\EntityClassInitialSetup;
use App\Location;
use App\Manufacturer;
use App\Supplier;

$factory->define(App\User::class, function (Faker\Generator $faker) {
    static $password;

    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'password' => $password ?: $password = bcrypt('secret'),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Location::class, function (Faker\Generator $faker) {

    //static $locationStateMin = 1;
    //static $locationStateMax = 4;

    return [
        'name' => $faker->unique()->regexify('S[ABCDEF][12345]\.[1-9][0-9]'),
        'room' => $faker->buildingNumber,
        //'state_id' => $faker->numberBetween($locationStateMin, $locationStateMax)
    ];
});

$factory->define(App\Manufacturer::class, function (Faker\Generator $faker) {

    return [
        'name' => $faker->unique()->company,
    ];
});

$factory->define(App\Supplier::class, function (Faker\Generator $faker) {

    return [
        'name' => $faker->unique()->company,
    ];
});

$factory->define(App\Entity::class, function (Faker\Generator $faker) {
    // exclude entity type classes
    return generateEntityBetween($faker, EntityClassInitialSetup::FIRST_DEVICE_CLASS_ID, EntityClassInitialSetup::LAST_DEVICE_CLASS_ID);
});

$factory->define(App\EntityParameter::class, function (Faker\Generator $faker) {
    return [

    ];
});

/**
 * @param \Faker\Generator $faker
 * @param int $entityClassIdFrom
 * @param int $entityClassIdTo
 * @param bool $mustNotHaveParent
 * @return array
 * @internal param null $parentEntityId
 */
function generateEntityBetween(Faker\Generator $faker, int $entityClassIdFrom, int $entityClassIdTo, $mustNotHaveParent = false)
{

    static $lastEntityId = null;

    static $entityStateMin = 1;
    static $entityStateMax = 2;

    if ($lastEntityId === null) {
        $lastEntityId = DB::table('entities')->max('id');
        if ($lastEntityId === null)
            $lastEntityId = 0;

    }

    // Choose a entity class
    $entityClassId = $faker->numberBetween($entityClassIdFrom, $entityClassIdTo);
    $entityClass = EntityClass::with('parentEntityClasses')->find($entityClassId);

    if ($mustNotHaveParent === true)
        $parentEntityId = null;
    else
        $parentEntityId = function () use ($faker, $entityClass) {

            if (!$entityClass->requireParent()) {

                // 30 % of entities which doesn't require parent will not have parent
                if ($faker->numberBetween(1, 10) <= 3)
                    return null;
            }

            $mustNotHaveParents = [];
            $possibleParents = [];
            foreach ($entityClass->parentEntityClasses as $parentEntityClass) {
                $possibleParents[] = $parentEntityClass->id;

                if ($parentEntityClass->pivot->parent_must_not_have_parent) {
                    $mustNotHaveParents[] = $parentEntityClass->id;
                }
            }

            if (empty($possibleParents))
                return null;

            $parentEntityClassId = $faker->randomElement($possibleParents);

            if (in_array($parentEntityClassId, $mustNotHaveParents)) {
                $mustNotHaveParent = true;
            } else
                $mustNotHaveParent = false;

            // Generate exact entity
            $parentEntityDefinition = generateEntityBetween($faker, $parentEntityClassId, $parentEntityClassId, $mustNotHaveParent);

            return factory(Entity::class)->create($parentEntityDefinition)->id;

        };

    $parentEntityTypeId = function () use ($faker, $entityClass) {

        $entityTypeClassId = $entityClass->entityTypeClassId();

        if ($entityTypeClassId === null)
            return null;

        // Generate exact entity
        $entityTypeDefinition = generateEntityBetween($faker, $entityTypeClassId, $entityTypeClassId);

        return factory(Entity::class)->create($entityTypeDefinition)->id;
    };

    $locationId = function () use ($faker, $entityClass) {

        if (!$entityClass->hasLocation())
            return null;

        // 10 % of entities will not have location assigned
        if ($faker->numberBetween(1, 10) === 1)
            return null;

        // Select random location
        $location = Location::find($faker->randomNumber(2));

        if ($location)
            return $location->id;

        // If location with random number not found, create new one ..
        return factory(Location::class)->create()->id;
    };
    $manufacturerId = function () use ($faker, $entityClass) {

        if (!$entityClass->hasManufacturer())
            return null;

        // 10 % of entities will not have manufacturer assigned
        if ($faker->numberBetween(1, 10) === 1)
            return null;

        // Select random manufacturer
        $manufacturer = Manufacturer::find($faker->randomNumber(2));

        if ($manufacturer)
            return $manufacturer->id;

        // If manufacturer with random number not found, create new one ..
        return factory(Manufacturer::class)->create()->id;

    };
    $supplierId = function () use ($faker, $entityClass) {

        if (!$entityClass->hasSupplier())
            return null;

        // 10 % of entities will not have supplier assigned
        if ($faker->numberBetween(1, 10) === 1)
            return null;

        // Select random supplier
        $supplier = Supplier::find($faker->randomNumber(2));

        if ($supplier)
            return $supplier->id;

        // If supplier with random number not found, create new one ..
        return factory(Supplier::class)->create()->id;

    };

    return [
        'id' => ++$lastEntityId,
        'parent_entity_id' => $parentEntityId,
        'entity_type_id' => $parentEntityTypeId,
        'entity_class_id' => $entityClassId,
        'location_id' => $locationId,
        'manufacturer_id' => $manufacturerId,
        'supplier_id' => $supplierId,
        'state_id' => $faker->numberBetween($entityStateMin, $entityStateMax)
    ];
}