<?php

namespace Fivable\Buildable\Models;

use App\User;
use Fivable\Buildable\Traits\HasMetas;
use Fivable\Buildable\Traits\IsPublishable;
use Illuminate\Database\Eloquent\Collection;
Use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;

class BuildablePage extends Model
{
    use SoftDeletes, IsPublishable, HasMetas;

    /* Columns that are mass-assignable */
    protected $fillable = ['slug', 'parent_id', 'user_id', 'title', 'content', 'status_mask', 'inner_nav_id', 'is_homepage', 'active_user_id'];
    /* Columns to convert to Carbon */
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];
    /* Automatically append these relationships */
    protected $with = ['active_user'];


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

    /**
     * Get the BuildableComponent's associated with this BuildablePage
     * @return HasMany
     */
    public function components() : HasMany
    {
        return $this->hasMany('Fivable\Buildable\Models\BuildableComponent', 'page_id');
    }

    /**
     * Get the parent BuildablePage of this BuildablePage
     * @return BelongsTo
     */
    public function parent() : BelongsTo
    {
        return $this->belongsTo('Fivable\Buildable\Models\BuildablePage', 'parent_id');
    }

    /**
     * Get the User that is associated with this BuildablePage
     * @return BelongsTo
     */
    public function user() : BelongsTo
    {
        return $this->belongsTo('App\User');
    }

    /**
     * Get the user currently editing the page, or NULL
     * @return HasOne|NULL
     */
    public function active_user() : ?HasOne
    {
        return $this->hasOne('App\User', 'id','active_user_id');
    }

    /**
     * Get the inner nav BuildableMenu associated with this page
     * @return BelongsTo
     */
    public function inner_nav() : BelongsTo
    {
        return $this->belongsTo('Fivable\Buildable\Models\BuildableMenu', 'inner_nav_id');
    }


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

    /**
     * Generate an index query for BuildablePages using options
     * @param array|NULL $status_masks
     * @return Collection
     */
    public static function indexFor(array $status_masks = NULL) : ?Collection
    {
        return BuildablePage::when(!is_null($status_masks), function($query) use ($status_masks) {
            $query->whereIn('status_mask', $status_masks);
        })->with('user')->get();
    }

    /**
     * Creates a new BuildablePage along with it's BuildableComponents
     * @param array $pageInfo
     * @param User $user
     * @return BuildablePage
     */
    public static function createPage(array $pageInfo, User $user) : BuildablePage
    {
        $page = static::create([
            'slug' => $pageInfo['slug'],
            'parent_id' => $pageInfo['parent_id'] ?? NULL,
            'user_id' => $user->id,
            'title' => $pageInfo['title'],
            'status_mask' => $pageInfo['status_mask'],
        ]);

        $page->saveMetas($pageInfo['metas']);

        $components = $pageInfo['components'] ?? NULL;

        foreach ($components as $component) {
            BuildableComponent::create([
                'name' => $component['name'],
                'slug' => $component['slug'],
                'content' => json_encode($component['content']),
                'order' => $component['order'],
                'vault_component_id' => $component['vault_component_id'] ?? NULL,
                'page_id' => $page->id,
                'is_grouped' => $component['is_grouped'],
            ]);
        }

        return $page;
    }

    /**
     * Update a BuildablePage, also updating or deleting it's BuildableComponents
     * @param array $pageInfo
     * @return BuildablePage
     */
    public function updatePage($pageInfo) : BuildablePage
    {
        $this->update([
            'slug' => $pageInfo['slug'],
            'parent_id' => $pageInfo['parent_id'] ?? NULL,
            'title' => $pageInfo['title'],
            'status_mask' => $pageInfo['status_mask'],
        ]);

        $this->updateMetas($pageInfo['metas']);

        $components = $pageInfo['components'] ?? NULL;

        foreach ($components as $component) {
            if (isset($component['id'])) {
                $existingComponent = BuildableComponent::find($component['id']);
                $existingComponent->update([
                    'name' => $component['name'],
                    'slug' => $component['slug'],
                    'content' => json_encode($component['content']),
                    'order' => $component['order'],
                    'vault_component_id' => $component['vault_component_id'] ?? NULL,
                    'is_grouped' => $component['is_grouped'],
                ]);
            } else {
                BuildableComponent::create([
                    'name' => $component['name'],
                    'slug' => $component['slug'],
                    'content' => json_encode($component['content']),
                    'order' => $component['order'],
                    'vault_component_id' => $component['vault_component_id'] ?? NULL,
                    'page_id' => $this->id,
                    'is_grouped' => $component['is_grouped'],
                ]);
            }
        }


        foreach ($pageInfo['deleted_components'] as $componentId) {
            $component = BuildableComponent::find($componentId);
            $component->delete();
        }

        return $this;
    }

    /**
     * Remove current homepage status and make this the homepage
     */
    public function makeHomepage()
    {
        BuildablePage::where('is_homepage', 1)->update(['is_homepage' => 0]);
        $this->update(['is_homepage' => 1]);
    }

    /**
     * Delete this BuildablePage's BuildableComponents and then the BuildablePage itself
     * @return bool|null|void
     * @throws \Exception
     */
    public function delete()
    {
        $this->components()->delete();
        parent::delete();
    }


    /** Misc Helpers
     * ---------------*/

    /**
     * Get the home page URL, if it exists
     * @return null|string
     */
    public static function homePageUrl() : ?string
    {
        $homePage = BuildablePage::homePage();
        return !is_null($homePage) ? ('/' . $homePage->slug): NULL;
    }

    /**
     * Get the home page
     * @return NULL|BuildablePage
     */
    public static function homePage() : ?BuildablePage
    {
        return static::whereNull('deleted_at')->where('is_homepage', 1)->first();
    }

    /** Laravel Overrides
     * ---------------------*/

    /**
     * Use BuildablePage's slug for Route Model Binding
     * @return string
     */
    public function getRouteKeyName()
    {
        return 'slug';
    }

}
