<?php

namespace MtcPharmacy\Multisite\Classes;

use Illuminate\Support\Facades\DB;
use Illuminate\Contracts\Database\Query;


class MultisiteManager
{
    private $seed_concrete_entity;

    public function __construct(object $seed_concrete_entity)
    {
        $this->seed_concrete_entity = $seed_concrete_entity;
        if (! $this->getSeedEntityConcreteType()) {
            throw new \Exception('Unknown type of object used to instantiate Multisite Manager.');
        }
    }


    public function getToggleHtml($allow_mode_switching = true)
    {
        return app('twig')->render('admin/multisite_toggle.twig', [
            'allow_mode_switching' => $allow_mode_switching,
            'multisite_manager' => $this,
            'single_site_mode_label' => MultisiteConstants::MODE_SINGLE_SITE,
            'multi_site_mode_label' => MultisiteConstants::MODE_MULTI_SITE,
        ]);
    }


    public static function getQueryJoin(string $entity_type) : string
    {
        $concrete_entity_id_field = self::getConcreteEntityIdField($entity_type);

        $base_entity_table = MultisiteBaseEntity::getTableName();

        $query_join = "
            JOIN {$base_entity_table} ms_base ON (
                ms_base.concrete_entity_type = '{$entity_type}' AND
                ms_base.concrete_entity_id = {$concrete_entity_id_field}
            )
        ";

        return $query_join;
    }


    public static function decorateQueryBaseOnly(Query\Builder $query_builder, string $entity_type) : void
    {
        $concrete_entity_id_field = self::getConcreteEntityIdField($entity_type);

        $base_entity_table = MultisiteBaseEntity::getTableName();
        $query_builder
            ->join("{$base_entity_table} AS ms_base", function($join) use ($entity_type, $concrete_entity_id_field) {
                $join
                    ->on('ms_base.concrete_entity_type', '=', DB::raw("'{$entity_type}'"))
                    ->on('ms_base.concrete_entity_id',   '=', $concrete_entity_id_field)
                ;
            })
        ;
    }


    private static function decorateQueryWithMultisiteInfoAddJoins(Query\Builder $query_builder, string $entity_type) : void
    {
        $concrete_entity_id_field = self::getConcreteEntityIdField($entity_type);

        $base_entity_table = MultisiteBaseEntity::getTableName();
        $query_builder
            ->leftJoin("{$base_entity_table} AS ms_base", function($join) use ($entity_type, $concrete_entity_id_field) {
                $join
                    ->on('ms_base.concrete_entity_type', '=', DB::raw("'{$entity_type}'"))
                    ->on('ms_base.concrete_entity_id',   '=', $concrete_entity_id_field)
                ;
            })

            ->leftJoin('multisite__custom_entities AS ms_cust', function($join) use ($entity_type, $concrete_entity_id_field) {
                $join
                    ->on('ms_cust.concrete_entity_id',   '=', $concrete_entity_id_field)
                ;
            })

            ->leftJoin("{$base_entity_table} AS ms_base2", 'ms_base2.id', '=', 'ms_cust.parent_entity_id')

            ->leftJoin('multisite__sites AS ms_sites', 'ms_sites.id', '=', 'ms_cust.site_id')
        ;
    }


    private static function decorateQueryWithMultisiteInfoAddSelects(Query\Builder $query_builder) : void
    {
        $query_builder
            ->selectRaw('IF (ms_cust.site_id IS NOT NULL, "custom", ms_base.mode)  AS ms_mode')
            ->selectRaw('ms_sites.name                                            AS ms_site_name')
            ->selectRaw('ms_sites.id                                              AS ms_site_id')
        ;
    }


    private static function decorateQueryWithMultisiteInfoAddWheres(Query\Builder $query_builder, bool $use_site_id = false) : void
    {
        $query_builder
            ->where(function(Query\Builder $query) use ($use_site_id) {
                $query
                    ->where('ms_base.mode', '=', DB::raw("'" . MultisiteConstants::MODE_SINGLE_SITE . "'"))
                    ->orWhere(function (Query\Builder $query) use ($use_site_id) {
                        $query
                            ->whereNotNull('ms_cust.site_id')
                            ->where('ms_base2.mode', '=', DB::raw("'" . MultisiteConstants::MODE_MULTI_SITE . "'"))
                        ;

                        if ($use_site_id) {
                            $query->where('ms_cust.site_id', '=', DB::raw(SITE_ID));
                        }
                    })
                ;
            })
        ;
    }


    public static function decorateQueryWithMultisiteInfo(Query\Builder $query_builder, string $entity_type, bool $use_site_id = false) : void
    {
        self::decorateQueryWithMultisiteInfoAddJoins($query_builder, $entity_type);
        self::decorateQueryWithMultisiteInfoAddSelects($query_builder);
        self::decorateQueryWithMultisiteInfoAddWheres($query_builder, $use_site_id);
    }


    public static function getMultisiteInfoDecorators(string $entity_type) : array
    {
        $decorators = [
            'joins' => '',
            'wheres' => '',
            'selects' => '',
        ];

        $q = \Illuminate\Support\Facades\DB::query();
        self::decorateQueryWithMultisiteInfoAddJoins($q, $entity_type);
        $decorators['joins'] = str_replace('select * ', '', $q->toSql());

        $q = \Illuminate\Support\Facades\DB::query();
        self::decorateQueryWithMultisiteInfoAddWheres($q, true);
        $decorators['wheres'] = str_replace('select * where ', 'AND ', $q->toSql());

        $q = \Illuminate\Support\Facades\DB::query();
        self::decorateQueryWithMultisiteInfoAddSelects($q);
        $decorators['selects'] = str_replace('select ', '', $q->toSql());

        return $decorators;
    }


    public function getSeedEntityConcreteType() : ?string
    {
        $type = null;

        $class_name = get_class($this->seed_concrete_entity);
        switch ($class_name) {
            case \Mtc\Shop\Brand::class:
                $type = MultisiteConstants::ENTITY_TYPE_SHOP_BRAND;
                break;

            case \Mtc\Shop\Category::class:
                $type = MultisiteConstants::ENTITY_TYPE_SHOP_CATEGORY;
                break;

            case \Mtc\Shop\Item::class:
                $type = MultisiteConstants::ENTITY_TYPE_SHOP_ITEM;
                break;

            case \Mtc\Cms\Models\Page::class:
                $type = MultisiteConstants::ENTITY_TYPE_CMS_PAGE;
                break;

            case \Mtc\Plugins\SiteMenu\Classes\SiteMenu::class:
                $type = MultisiteConstants::ENTITY_TYPE_SITE_MENU;
                break;
        }

        return $type;
    }


    public function isEntityInSingleSiteMode() : bool
    {
        return ($this->getBaseEntity()->mode == MultisiteConstants::MODE_SINGLE_SITE);
    }


    public function getSelectedSite() : ?MultisiteSite
    {
        $selected_site = null;

        $base_entity = $this->getBaseEntity();
        if ($base_entity->mode == MultisiteConstants::MODE_MULTI_SITE) {
            foreach ($base_entity->custom_entities as $custom_entity) {
                if ($custom_entity->concrete_entity_id == $this->seed_concrete_entity->id) {
                    $selected_site = $custom_entity->site;
                }
            }
        }

        return $selected_site;
    }


    public function getBaseEntity($create_if_missing = true) : ?MultisiteBaseEntity
    {
        $base_entity = MultisiteBaseEntity::query()
            ->where('concrete_entity_type', $this->getSeedEntityConcreteType())
            ->where('concrete_entity_id', $this->seed_concrete_entity->id)
            ->first()
        ;

        if (! $base_entity) {
            $base_entity_table = MultisiteBaseEntity::getTableName();
            $custom_entity = MultisiteCustomEntity::query()
                ->join("{$base_entity_table} AS ms_base", 'ms_base.id', '=', 'multisite__custom_entities.parent_entity_id')
                ->where('ms_base.concrete_entity_type', $this->getSeedEntityConcreteType())
                ->where('multisite__custom_entities.concrete_entity_id', $this->seed_concrete_entity->id)
                ->first()
            ;

            if ($custom_entity) {
                $base_entity = $custom_entity->parent_entity;
            }
        }

        if (! $base_entity && $create_if_missing) {
            $base_entity = $this->createBaseEntity();
        }

        return $base_entity;
    }


    private function createBaseEntity()
    {
        $base_entity = MultisiteBaseEntity::create([
            'concrete_entity_id' => $this->seed_concrete_entity->id,
            'concrete_entity_type' => $this->getSeedEntityConcreteType(),
            'mode' => MultisiteConstants::MODE_SINGLE_SITE,
        ]);

        return $base_entity;
    }


    public function getCustomEntityData() : array
    {
        $data = [];

        foreach ($this->getBaseEntity()->custom_entities as $custom_entity) {
            $data[$custom_entity->site->name] = $custom_entity->getConcreteEntity();
        }

        return $data;
    }


    private function getSwitchModeUrl(string $target_mode) : ?string
    {
        $url = null;

        $url = route('admin-multisite-switch-mode', [
            'target_mode' => $target_mode,
            'concrete_entity_type' => $this->getBaseEntity()->concrete_entity_type,
            'base_entity_id' => $this->getBaseEntity()->id,
        ], false);

        return $url;
    }


    public function getToggleModeUrl() : ?string
    {
        $target_mode = null;

        if ($this->isEntityInSingleSiteMode()) {
            $target_mode = MultisiteConstants::MODE_MULTI_SITE;
        } else {
            $target_mode = MultisiteConstants::MODE_SINGLE_SITE;
        }

        return $this->getSwitchModeUrl($target_mode);
    }


    public function getDisplayEntity()
    {
        $base_entity = $this->getBaseEntity();

        switch ($base_entity->mode) {
            case MultisiteConstants::MODE_SINGLE_SITE:
                $display_entity = $base_entity;
                break;

            case MultisiteConstants::MODE_MULTI_SITE:
                $display_entity = $base_entity
                    ->custom_entities
                    ->where('site_id', SITE_ID)
                    ->first()
                ;
                break;
        }

        return $display_entity->getConcreteEntity();
    }


    private function getSeedEntity() : ?object
    {
        $concrete_entity_type = $this->getSeedEntityConcreteType();
        $concrete_entity_id = $this->seed_concrete_entity->id;

        $base_entity_table = MultisiteBaseEntity::getTableName();

        $seed_entity = MultisiteCustomEntity::query()
            ->join("{$base_entity_table} AS ms_base", 'ms_base.id', '=', 'multisite__custom_entities.parent_entity_id')
            ->where('ms_base.concrete_entity_type', $concrete_entity_type)
            ->where('multisite__custom_entities.concrete_entity_id', $concrete_entity_id)
            ->first()
        ;

        if (! $seed_entity) {
            $seed_entity = MultisiteBaseEntity::query()
                ->where('concrete_entity_type', $concrete_entity_type)
                ->where('concrete_entity_id', $concrete_entity_id)
                ->first()
            ;
        }

        return $seed_entity;
    }


    public function getSeedEntityMode() : ?string
    {
        $mode = null;

        $seed_entity = $this->getSeedEntity();

        if ($seed_entity instanceof MultisiteCustomEntity) {
            $mode = MultisiteConstants::MODE_MULTI_SITE;
        } else {
            $mode = MultisiteConstants::MODE_SINGLE_SITE;
        }

        return $mode;
    }


    public function ensureTheCorrectAdminPageLoaded()
    {
        if ($this->getBaseEntity()->mode != $this->getSeedEntityMode()) {
            $display_item = $this->getDisplayEntity();

            header('Location: ' . $display_item->getAdminUrl());
            exit;
        }
    }


    public function normaliseSlugs(string $slug)
    {
        $base_entity = $this->getBaseEntity();

        $d = $base_entity->getConcreteEntity();
        $d->updateSlug($slug);

        foreach ($base_entity->custom_entities as $custom_entity) {
            $c = $custom_entity->getConcreteEntity();
            $c->updateSlug($slug);
        }
    }


    public function normaliseCategories(array $category_ids)
    {
        $base_entity = $this->getBaseEntity();

        $e = $base_entity->getConcreteEntity();
        $e->categories()->sync($category_ids);

        foreach ($base_entity->custom_entities as $custom_entity) {
            $e = $custom_entity->getConcreteEntity();
            $e->categories()->sync($category_ids);
        }
    }


    public function normaliseBrands(array $brand_ids)
    {
        $base_entity = $this->getBaseEntity();

        $e = $base_entity->getConcreteEntity();
        $e->brands()->sync($brand_ids);

        foreach ($base_entity->custom_entities as $custom_entity) {
            $e = $custom_entity->getConcreteEntity();
            $e->brands()->sync($brand_ids);
        }
    }


    private static function getConcreteEntityIdField(string $entity_type) : string
    {
        $concrete_entity_id_field = null;

        switch ($entity_type) {
            case MultisiteConstants::ENTITY_TYPE_SHOP_BRAND:
                $concrete_entity_id_field = 'brands.id';
                break;

            case MultisiteConstants::ENTITY_TYPE_SHOP_CATEGORY:
                $concrete_entity_id_field = 'categories.id';
                break;

            case MultisiteConstants::ENTITY_TYPE_SHOP_ITEM:
                $concrete_entity_id_field = 'items.id';
                break;

            case MultisiteConstants::ENTITY_TYPE_CMS_PAGE:
                $concrete_entity_id_field = 'pages.id';
                break;
        }

        return $concrete_entity_id_field;
    }
}
