<?php

namespace Fivable\Buildable\Builders;

use Fivable\Buildable\Models\BuildableComponent;
use GuzzleHttp\Client;

class ComponentBuilder
{
    // The component being built
    protected $component;
    // The component's row of data from a BuildablePage's content
    protected $content;
    // The data for a corresponding Vue Component
    protected $data;
    // Does this component have a google map?
    public $hasMaps = false;

    // Scripts required by a component, in order of execution
    public $componentScripts = [];

    /**
     * Instantiate a new Component Builder
     * @param BuildableComponent $buildableComponent
     */
    public function __construct(BuildableComponent $buildableComponent)
    {
        $this->data = new \stdClass;
        $this->component = $buildableComponent;
        $this->index = $buildableComponent->index;
        $this->content = json_decode($buildableComponent->content);
        $this->content->properties = static::keyProperties($this->content->properties);
        $this->data = $this->content;
        $this->build();
    }

    /**
     * Use the BuildableComponent's name to call its build function
     */
    public function build()
    {
        // If any additional PHP-crunching needs to happen it will be in this function
        // Use a component function to add extra data to the $this->data for instance
        $componentBuildFunction = $this->getFunctionName();
        if (method_exists($this, $componentBuildFunction)) {
            $this->$componentBuildFunction();
        }
    }

    /**
     * Get the name of this component's build function
     * @return string
     */
    public function getFunctionName() : string
    {
        $words = explode("_", ('buildable_' . $this->component->slug));
        for ($i = 0; $i < count($words); $i++) {
            if ($i != 0) {
                $words[$i] = ucfirst($words[$i]);
            }
        }
        return implode('', $words);
    }

    /**
     * Take properties array and return keyed properties array
     * @param array $properties
     * @return array
     */
    public static function keyProperties(array $properties) : array
    {
        $keyedProperties = [];
        foreach ($properties as $property) {
            $keyedProperties{$property->name} = $property;
        }

        return $keyedProperties;
    }

    /**
     * Add a BuildableComponent script to the ComponentRenderer with an optional execution order
     * @param string $scriptName
     * @param int $executionOrder
     */
    public function addComponentScript(string $scriptName, int $executionOrder = 5)
    {
        $this->componentScripts[$executionOrder][] = $scriptName;
    }

    /**
     * Spit out any BuildableComponent scripts added during $this->build()
     * @return array
     */
    public function getComponentScripts() : array
    {
        return $this->componentScripts;
    }

    /**
     * Get the BuildableComponent being built
     * @return BuildableComponent
     */
    public function getComponent() : BuildableComponent
    {
        return $this->component;
    }

    /**
     * Get the data meant for the Vue Component
     * @return string
     */
    public function getComponentData() : string
    {
        return json_encode($this->data);
    }

    /**
     * Get the component's formatted name (used for tag)
     * @return string
     */
    public function getTagName() : string
    {
        return 'buildable-' . str_replace('_', '-', $this->component->slug);
    }

    /**
     * Does this component have angled top?
     * @return bool
     */
    public function hasAngledTop()
    {
        return $this->content->background->angledTop === true || $this->content->background->angledTop === 'true';
    }

    /**
     * Does this component have angled bottom?
     * @return bool
     */
    public function hasAngledBottom()
    {
        return $this->content->background->angledBottom === true || $this->content->background->angledBottom === 'true';
    }

    /**
     * Returns the index of the component
     * @return int
     */
    public function getIndex() : int
    {
      return $this->index;
    }

    /**
     * Return a string of the classes this component needs
     * @return string
     */
    public function getClasses() : string
    {
        $name = $this->component->slug;
        $backgroundColor = $accentColor = $size = $fixed = $angledTop = $angledBottom = '';
        extract((array)$this->content->colors);
        extract((array)$this->content->background);
        extract((array)$this->content->additionalOptions);
        $backgroundColor = (!empty($backgroundColor)) ? str_replace('-color', '', $backgroundColor) : '';
        $backgroundColorClass = (!empty($backgroundColor) ? $backgroundColor . '-theme' : '');
        if (!empty($accentColor)) {
            $accentColor = str_replace('-color', '', $accentColor);
            $backgroundColorClass .= " $accentColor-accent";
        }
        $bgSizeClass = (!empty($size) ? 'bg-' . $size : '');
        $bgFixedClass = $fixed === true || $fixed === 'true' ? 'bg-fixed' : '';
        $removeTopPaddingClass = $removeTopPadding === true || $removeTopPadding === 'true' ? 'no-padding-top' : '';
        $removeBottomPaddingClass = $removeBottomPadding === true || $removeBottomPadding === 'true' ? 'no-padding-bottom' : '';
        $negativeTopMarginClass = $negativeTopMargin === true || $negativeTopMargin === 'true' ? 'negative-margin-top' : '';
        $negativeBottomMarginClass = $negativeBottomMargin === true || $negativeBottomMargin === 'true' ? 'negative-margin-bottom' : '';
        $hideOnMobileClass = $hideOnMobile === true || $hideOnMobile === 'true' ? 'hide-mobile' : '';
        $hideOnDesktopClass = $hideOnDesktop === true || $hideOnDesktop === 'true' ? 'hide-desktop' : '';

        // TODO: Figure out why bg angled option saves as string
        $bgAngledClasses = $this->hasAngledTop() ? 'angle--top-left ' : '';
        $bgAngledClasses .= $this->hasAngledBottom() ? 'angle--bottom-right ' : '';
        if ($this->hasAngledTop() && $this->hasAngledBottom()) {
            $bgAngledClasses .= 'angle--both-left-right';
        }

        return "row $name-row $backgroundColorClass $bgSizeClass $bgFixedClass $bgAngledClasses $removeTopPaddingClass $removeBottomPaddingClass $negativeTopMarginClass $negativeBottomMarginClass $hideOnMobileClass $hideOnDesktopClass $classes";
    }

    /**
     * Get the component's "anchor"
     * @return NULL|string
     */
    public function getAnchor() : ?string
    {
        return $this->content->additionalOptions->anchor;
    }

    /**
     * Get the Background Image Style for the component
     * @return string
     */
    public function getBackgroundImageStyle() : string
    {
        $backgroundImage = $this->content->background->image;
        $backgroundImageUrl = (is_object($backgroundImage) ? $backgroundImage->url : $backgroundImage);
        return (!empty($backgroundImageUrl) ? "background-image: url($backgroundImageUrl);" : '');
    }


    /**
     * Add component-specific functions for extra data preparation
     * ----------------------------------------------------------------*/

    private function buildableDetailedImages()
    {
        unset($this->data->properties['Image & Details']->properties);
        if (isset($this->data->properties['Image & Details']->value)) {
            $this->data->properties['Images & Details'] = $this->data->properties['Image & Details']->value;
            unset($this->data->properties['Image & Details']);

            foreach ($this->data->properties['Images & Details'] as $listItem) {
                foreach ($listItem->properties as $property) {
                    $listItem->{$property->name} = $property;
                }
                unset($listItem->properties);
            }
        }
    }

    private function buildableFixedImageScrolling()
    {
        unset($this->data->properties['Image & Description']->properties);
        $this->data->properties['Images & Descriptions'] = $this->data->properties['Image & Description']->value;
        unset($this->data->properties['Image & Description']);

        foreach ($this->data->properties['Images & Descriptions'] as $listItem) {
            foreach ($listItem->properties as $property) {
                $listItem->{$property->name} = $property;
            }
            unset($listItem->properties);
        }
    }

    private function buildableLogoPromoSlider()
        {
            unset($this->data->properties['Image & URL']->properties);
            $this->data->properties['Images & URLs'] = $this->data->properties['Image & URL']->value;
            unset($this->data->properties['Image & URL']);

            foreach ($this->data->properties['Images & URLs'] as $listItem) {
                foreach ($listItem->properties as $property) {
                    $listItem->{$property->name} = $property;
                }
                unset($listItem->properties);
            }
        }

    private function buildableReflexiveList()
    {
        unset($this->data->properties['List Item']->properties);
        $this->data->properties['List Items'] = $this->data->properties['List Item']->value;
        unset($this->data->properties['List Item']);

        foreach ($this->data->properties['List Items'] as $listItem) {
            foreach ($listItem->properties as $property) {
                $listItem->{$property->name} = $property;
            }
            unset($listItem->properties);
        }
    }

    private function buildableReflexiveImageList()
    {
        unset($this->data->properties['List Item']->properties);
        $this->data->properties['List Items'] = $this->data->properties['List Item']->value;
        unset($this->data->properties['List Item']);

        foreach ($this->data->properties['List Items'] as $listItem) {
            foreach ($listItem->properties as $property) {
                $listItem->{$property->name} = $property;
            }
            unset($listItem->properties);
        }
    }

    private function buildableTestimonialSlider()
    {
        $this->data->properties['Quotes'] = $this->data->properties['Quote']->value;
        unset($this->data->properties['Quote']);

        foreach ($this->data->properties['Quotes'] as $quote) {
            foreach ($quote->properties as $property) {
                $quote->{$property->name} = $property;
            }
            unset($quote->properties);
        }
    }


    /**
     * Barnacle-specific component functions
     * ----------------------------------------*/

    private function buildableObfuscatedMap()
    {
        // Call Barnacle API for locations to put on the map
        $client = new Client(['headers' => ['Accept' => 'application/json'], 'verify' => false]);
        $response = $client->request('GET', config('buildable.barnacleAPI', 'localhost:8000/api') . '/obfuscated-map-locations');
        $responseBody = json_decode($response->getBody());
        $this->data->properties['Locations'] = $responseBody->locations;
        $this->hasMaps = true;
    }

    private function buildableCoolData()
    {
        // Call Barnacle API
        $client = new Client(['headers' => ['Accept' => 'application/json'], 'verify' => false]);
        $response = $client->request('GET', config('buildable.barnacleAPI', 'localhost:8000/api') . '/marketing-data');
        $responseBody = json_decode($response->getBody());
        $this->data->properties['Metrics'] = $responseBody->data;
    }
}
