<?php

namespace App\Support;

use App\Support\Traits\DateRanges;

class Query extends Entity
{
    use DateRanges;

    public $query;

    public $url;

    public function __construct($query = null, $url = null)
    {
        if (is_null($query)) {
            $query = request()->query();
        }

        parent::__construct($query);

        $this->query = new Entity($query);

        if (is_null($url)) {
            $url = url()->current();
        }

        $this->url = $url;
    }

    public function action($action, $parameters = [], $absolute = true)
    {
        $this->url = action($action, $parameters, $absolute);

        return $this;
    }

    /**
     * @param null $parameters
     * @return Query
     */
    public function build($parameters = null)
    {
        if (is_array($parameters) && is_numeric(key($parameters))) {
            $parameters = $this->only($parameters);
        }

        $query = with(new static($parameters, $this->url));

        return $query;
    }

    public function create($parameters = [])
    {
        $this->attributes = self::getArrayable($parameters);

        return $this->getFull();
    }

    public function current()
    {
        $this->url = url()->current();

        return $this;
    }

    public function except($keys)
    {
        $keys = is_array($keys) ? $keys : func_get_args();

        return new self(array_except($this->attributes, $keys));
    }

    public function only($keys)
    {
        $keys = is_array($keys) ? $keys : func_get_args();

        return new self(array_only($this->attributes, $keys));
    }

    public function getFull()
    {
        return $this->getUrl() . $this->getQuery();
    }

    /**
     * You should only use it when you're sure that parameters are safe and doesn't come with any injection values.
     * @return string
     */
    public function getFullDecoded()
    {
        return $this->getUrl() . $this->getDecodedQuery();
    }

    public function getQuery()
    {
        return $this->isEmpty() ? '' : '?' . http_build_query($this->all());
    }

    /**
     * You should only use it when you're sure that parameters are safe and doesn't come with any injection values.
     * @return string
     */
    public function getDecodedQuery()
    {
        return urldecode($this->getQuery());
    }

    public function getUrl()
    {
        return rtrim($this->url, '/');
    }

    public function parameters($parameters = [])
    {
        $this->attributes = self::getArrayable($parameters);

        return $this;
    }

    public function previous()
    {
        $this->url = url()->previous();

        return $this;
    }

    public function revert()
    {
        $this->attributes = self::getArrayable($this->query);

        return $this;
    }

    public function route($name, $parameters = [], $absolute = true, $route = null)
    {
        $this->url = route($name, $parameters, $absolute, $route);

        return $this;
    }

    public function to($parameters = [], $revert = true)
    {
        $url = $this->put($parameters)->getFull();

        if ($revert) {
            $this->attributes = self::getArrayable($this->query);
        }

        return $url;
    }

    public function update($parameters = [])
    {
        $this->merge($parameters);

        request()->replace($this->all());

        return $this;
    }

    public function url($path, $parameters = [])
    {
        $this->url = url($path, $parameters);

        return $this;
    }

    /**
     * @param bool $validateDates
     * @return self
     */
    public function setValidateDates(bool $validateDates): self
    {
        $this->validateDates = $validateDates;

        return $this;
    }
}
