<?php

/**
 * Questo file è parte di Pongho.
 *
 * @author    Daniele Termini
 * @copyright Web Agency Meta Line S.r.l.
 * @package   Application\Admin
 */

namespace Application\Admin\Utilities;

use Pongho\Utilities\DateTime;

class FilterConverter
{
    /**
     * Prepara una condizione per la query
     *
     * @param string $column
     * @param mixed  $value
     * @param string $field
     * @return array
     */
    public function toCondition($column, $value, $field = null)
    {
        if ($field === null) {
            $field = $this->quote($column);
        }

        if (is_array($value)) {
            // Sto indicando una tipologia di filtro
            $type = array_key_first($value);

            switch ($type) {
                case 'LIKE':
                    $condition = ['pongho_like(' . $field . ', :' . $column . ')', $column => '%' . $value[$type] . '%'];
                    break;

                case 'START':
                    $condition = ['pongho_like(' . $field . ', :' . $column . ')', $column => $value[$type] . '%'];
                    break;

                case 'END':
                    $condition = ['pongho_like(' . $field . ', :' . $column . ')', $column => '%' . $value[$type]];
                    break;

                case 'IN':
                    if (is_array($value[$type]) && $value[$type] !== []) {
                        $condition = [$field . ' IN (' . $this->getCsv($value[$type]) . ')'];
                    } else {
                        $condition = ['1 = 1'];
                    }

                    break;

                case 'NOTIN':
                    if (is_array($value[$type]) && $value[$type] !== []) {
                        $condition = [$field . ' NOT IN (' . $this->getCsv($value[$type]) . ')'];
                    } else {
                        $condition = ['1 = 1'];
                    }

                    break;

                case 'GT':
                    $condition = [$field . ' > :' . $column, $column => $value[$type]];
                    break;

                case 'GTE':
                    $condition = [$field . ' >= :' . $column, $column => $value[$type]];
                    break;

                case 'LT':
                    $condition = [$field . ' < :' . $column, $column => $value[$type]];
                    break;

                case 'LTE':
                    $condition = [$field . ' <= :' . $column, $column => $value[$type]];
                    break;

                case 'NOT':
                    $condition = [$field . ' <> :' . $column, $column => $value[$type]];
                    break;

                case 'NULL':
                    $condition = [$field . ' IS NULL'];
                    break;

                case 'NOTNULL':
                    $condition = [$field . ' IS NOT NULL'];
                    break;

                case 'DATEEQ':
                    try {
                        $date = new DateTime($value[$type]);

                        // Se non ho l'ora è un between - mi aspetto che il formato sia Y-m-d o Y-m-d H:i[:s]
                        if (($date->format('H:i:s') === '00:00:00') && strlen((string) $value[$type]) === 10) {
                            $dates = [
                                $date->format('Y-m-d H:i:s'),
                                $date->add(new \DateInterval('PT23H59M59S'))->format('Y-m-d H:i:s'),
                            ];

                            $condition = $this->handleBaseCondition(json_encode($dates), $column, $field);
                        } else {
                            // Altrimenti è un'uguaglianza sulla data
                            $condition = $this->handleBaseCondition($date->format('Y-m-d H:i:s'), $column, $field);
                        }
                    } catch (\Exception) {
                        $condition = ['1 = 0'];
                    }

                    break;
                case 'DATEBETWEEN':
                    // Deve essere un intervallo!
                    if (!is_array($value[$type])) {
                        $condition = ['1 = 0'];
                        break;
                    }

                    try {
                        $dates = $value[$type];

                        array_walk( // vabbè.. sono solo 2 elementi
                            $dates,
                            function (&$value, $key): void {
                                $date = new DateTime($value);

                                // Se non ho l'ora... - mi aspetto che il formato sia Y-m-d o Y-m-d H:i[:s]
                                if (($date->format('H:i:s') === '00:00:00') && ($key === 1) && strlen((string) $value) === 10) {
                                    $date->add(new \DateInterval('PT23H59M59S'))->format('Y-m-d H:i:s');
                                }

                                $value = $date->format('Y-m-d H:i:s');
                            }
                        );

                        $condition = $this->handleBaseCondition(json_encode($dates), $column, $field);
                    } catch (\Exception) {
                        $condition = ['0 = 1']; // è come sopra ma permette di capire se è uscito per un errore
                    }

                    break;

                default:
                    if (is_array($value[$type])) {
                        throw new \RuntimeException('Filter type not implemented: ' . $type);
                    }

                    // È un uguaglianza o un intervallo
                    $condition = $this->handleBaseCondition($value[$type], $column, $field);
                    break;
            }
        } else {
            // È un uguaglianza o un intervallo
            $condition = $this->handleBaseCondition($value, $column, $field);
        }

        return $condition;
    }

    /**
     * Gestisce la condizione di base EQ (ricerca semplice)
     * @param $value
     * @param $name
     * @param $field
     * @return array
     */
    protected function handleBaseCondition($value, $name, $field)
    {
        $v = json_decode((string) $value);

        if (is_array($v)) {
            $condition = [
                $field . ' BETWEEN :' . $name . '_l AND :' . $name . '_r',
                $name . '_l' => $v[0],
                $name . '_r' => $v[1],
            ];
        } elseif ($value === '' || $value === null) {
            $condition = ["($field = '' OR $field IS NULL)"];
        } elseif ($value === 'T') {
            $condition = ["$field = :$name", $name => true];
        } elseif ($value === 'F') {
            $condition = ["$field = :$name", $name => false];
        } elseif ($value === 'null') {
            $condition = ["$field IS NULL"];
        } else {
            $condition = [$field . ' = :' . $name, $name => $value];
        }

        return $condition;
    }

    /**
     * Restituisce un array di valori in formato CSV e quotato
     *
     * @param $value
     * @return string
     */
    protected function getCsv($value)
    {
        return implode(
            ', ',
            array_map(
                function ($v) {
                    if (is_numeric($v)) {
                        return $v;
                    }

                    return "'{$v}'";
                },
                $value
            )
        );
    }

    /**
     * Restituisce il nome della colonna o identificatore con le virgolette in modo da non incappare in casi
     * in cui il nome corrisponde ad un identificatore riservato (from, key, left...)
     *
     * @param $name
     * @return string
     */
    protected function quote($name)
    {
        return "`{$name}`";
    }
}
