<?php

namespace Ignite\Core\Models\Form;

use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use Kris\LaravelFormBuilder\Fields\FormField;

class Backend
{
    use Macroable;

    /** @var string */
    protected $value;

    /**
     * Backend.
     *
     * @param string $value
     */
    public function __construct($value)
    {
        $this->value = $this->clean($value);
    }

    /**
     * Clean the value.
     *
     * @param  mixed $value
     * @return mixed
     */
    protected function clean($value)
    {
        if (is_string($value)) {
            $value = $this->cleanString($value);
        }

        if (is_array($value)) {
            $value = $this->cleanArray($value);
        }

        return $value;
    }

    /**
     * Clean the string value.
     *
     * @param  string $value
     * @return string
     */
    protected function cleanString($value)
    {
        $value = trim($value);
        $value = strip_tags($value);
        $value = filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);

        return $value;
    }

    /**
     * Clean the string value.
     *
     * @param  array $value
     * @return array
     */
    protected function cleanArray($value)
    {
        return array_map(function ($item) {
            return $this->cleanString($item);
        }, $value);
    }

    /**
     * Apply the type negotiation.
     *
     * @param  FormField $field
     * @return mixed
     */
    public function apply(FormField $field)
    {
        return call_user_func_array([$this, $field->getOption('backend_type') ?? 'string'], compact('field'));
    }

    /**
     * Handle a string type.
     *
     * @param  FormField $field
     * @return string
     */
    protected function string(FormField $field)
    {
        // Possibly transliterate, swap diacritics, if necessary.

        // Check if the value is an array, if so, implode it as comma-separated
        if (is_array($this->value)) {
            $this->value = implode(',', $this->value);
        }

        return $this->value;
    }

    /**
     * Handle a string type.
     *
     * @param  FormField $field
     * @return array
     */
    protected function array(FormField $field)
    {
        return $this->cleanArray($this->value);
    }

    /**
     * Handle a password type.
     *
     * @param  FormField $field
     * @return string
     */
    protected function password(FormField $field)
    {
        return $this->value;
    }

    /**
     * Handle a textarea type.
     *
     * @param  FormField $field
     * @return string
     */
    protected function textarea(FormField $field)
    {
        // Possibly transliterate, swap diacritics, if necessary.

        return $this->value;
    }

    /**
     * Handle a blob type.
     *
     * @param  FormField $field
     * @return string
     */
    protected function blob(FormField $field)
    {
        return $this->value;
    }

    /**
     * Handle a integer type.
     *
     * @param  FormField $field
     * @return int
     */
    protected function int(FormField $field)
    {
        return (int) $this->removeNonNumericDisallowedCharacters();
    }

    /**
     * @param FormField $field
     *
     * @return int
     */
    protected function boolean(FormField $field)
    {
        if ($this->value === true) {
            return 1;
        }

        if ($this->value === false) {
            return 0;
        }

        return (int) $this->value;
    }

    /**
     * Handle a double type.
     *
     * @param  FormField $field
     * @return float
     */
    protected function double(FormField $field)
    {
        return (float) $this->removeNonNumericDisallowedCharacters();
    }

    /**
     * Handle a date type.
     *
     * @param  FormField $field
     * @return string|null
     */
    protected function date(FormField $field)
    {
        return $this->normalizeDate($field, 'Y-m-d');
    }

    /**
     * Handle a datetime type.
     *
     * @param  FormField $field
     * @return string|null
     */
    protected function datetime(FormField $field)
    {
        return $this->normalizeDate($field, 'Y-m-d H:i:s');
    }

    /**
     * Normalize a date or datetime value.
     *
     * @param FormField $field
     * @param string $format
     * @param mixed $default
     *
     * @return mixed
     */
    protected function normalizeDate(FormField $field, $format, $default = null)
    {
        if (empty($this->value)) {
            return $default;
        }

        try {
            return Carbon::parse($this->value)->format($format);
        } catch (\Exception $e) {
            return $default;
        }
    }

    /**
     * Handle a timestamp type.
     *
     * @param  FormField $field
     * @return int
     */
    protected function timestamp(FormField $field)
    {
        return Carbon::createFromTimestamp($this->value)->getTimestamp();
    }

    /**
     * Remove all non numeric characters except for a period.
     *
     * @return string
     */
    protected function removeNonNumericDisallowedCharacters()
    {
        return preg_replace('/[^0-9.]/', '', $this->value);
    }
}
