<?php

namespace Fivable\Buildable\Traits;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;

trait HasMetas {

    /** Relationships
     * ----------------*/

    /**
     * Get all the metas associated with this model
     * @return HasMany
     */
    public function metas() : HasMany
    {
        if ($this->lowercaseEntityName() == 'user') {
            return $this->hasMany('Fivable\Buildable\Models\BuildableUserMeta', $this->entityForeignKey());
        }
        return $this->hasMany(get_class($this) . 'Meta', $this->entityForeignKey());
    }

    /**
     * Accessor for metas attribute
     * @return Collection
     */
    public function getMetasAttribute() : Collection
    {
        return $this->metas()->get();
    }


    /** CRUD Helpers
     * ----------------*/

    /**
     * Save new metas for a new model
     * @param array $metaContent
     */
    public function saveMetas(array $metaContent)
    {
        $metaModelName = $this->metaModelName();
        foreach ($metaContent as $meta) {
            $key = $meta['key'];
            $value = $meta['value'];

            $metaModelName::create([
                'key' => $key,
                'value' => $value,
                $this->entityForeignKey() => $this->id,
            ]);
        }
    }

    /**
     * Update or create new metas for existing instance of model
     * @param array $metaContent
     */
    public function updateMetas(array $metaContent)
    {
        $metaModelName = $this->metaModelName();
        foreach ($metaContent as $meta)
        {
            $key = $meta['key'];
            $value = $meta['value'];

            $metaObject = $metaModelName::where([
                [$this->entityForeignKey(), $this->id],
                ['key', $key]
            ])->first();

            if (is_null($metaObject)) {
                $metaModelName::create([
                    'key' => $key,
                    'value' => $value,
                    $this->entityForeignKey() => $this->id,
                ]);
            } else {
                $metaObject->update([
                    'value' => $value,
                ]);
            }
        }
    }

    /**
     * Get calling class meta 'short name'
     * @return string
     */
    public function metaModelName() : string
    {
        if ($this->lowercaseEntityName() == 'user') {
            return '\Fivable\Buildable\Models\BuildableUserMeta';
        }
        return '\\' . get_class($this) . 'Meta';
    }

    /**
     * Get the meta table's foreign key for the model
     * @return string
     */
    public function entityForeignKey() : string
    {
        return 'buildable_' . $this->lowercaseEntityName() . '_id';
    }

    /**
     * Get calling class 'short name' in lowercase
     * @return string
     */
    public function lowercaseEntityName() : string
    {
        $fullClassName = get_class($this);
        $positionOfSlash = strrpos($fullClassName, '\\');
        if (!$positionOfSlash) {
            $name = $fullClassName;
        } else {
            $name = substr($fullClassName, $positionOfSlash + 1);
        }

        $positionOfBuildable = strrpos($name, 'Buildable');
        if ($positionOfBuildable !== false) {
            $name = substr($name, $positionOfBuildable + 9);
        }

        return strtolower($name);
    }

    /**
     * Get a value or NULL for a meta key for this entity
     * @param string $metaKey
     * @return null|string
     */
    public function getMetaValue(string $metaKey) : ?string
    {
        $metaObject = $this->metas()->where('key', $metaKey)->first();
        return is_null($metaObject) ? NULL : $metaObject->value;
    }
}
