<?php

namespace Dipper\Foundation\Database;

use DateTime;
use DateTimeInterface;
use Illuminate\Support\Fluent;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Grammars\PostgresGrammar as Grammar;

class PostgresSchemaGrammar extends Grammar
{
    /**
     * Compile a create table command.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @return string
     */
    public function compileCreate(Blueprint $blueprint, Fluent $command)
    {
        $sql = [];

        $partition = '';

        if ($blueprint->partition) {
            $partition = sprintf('partition by %s (%s)', $blueprint->partition['type'], $blueprint->partition['column']);
        }

        $sql[] = sprintf(
            '%s table %s (%s) %s',
            $blueprint->temporary ? 'create temporary' : 'create',
            $this->wrapTable($blueprint),
            implode(', ', $this->getColumns($blueprint)),
            $partition
        );

        $comments = collect($blueprint->getAddedColumns())->where('comment', '<>', '')->all();

        foreach ($comments as $comment) {
            $sql[] = sprintf(
                "comment on column %s.%s is '%s'",
                $this->wrapTable($blueprint),
                $comment->name,
                $comment->comment
            );
        }

        return $sql;
    }

    /**
     * Compile a unique key command.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @return string
     */
    public function compileUnique(Blueprint $blueprint, Fluent $command)
    {
        return sprintf(
            'create unique index %s ON %s (%s)',
            $this->wrap($command->index),
            $this->wrapTable($blueprint),
            $this->columnize($command->columns)
        );
    }

    /**
     * Compile a unique key command.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @return string
     */
    public function compileComment(Blueprint $blueprint, Fluent $command)
    {
        return sprintf(
            "comment on table %s is '%s'",
            $this->wrapTable($blueprint),
            $command->comment
        );
    }

    /**
     * Compile a unique key command.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @return string
     */
    public function compileSetIncrement(Blueprint $blueprint, Fluent $command)
    {
        $column = collect($blueprint->getColumns())->where('autoIncrement', true)->first();

        if ($column) {
            $index = $this->wrap("{$blueprint->getTable()}_{$column->name}_seq");
            return sprintf("select setval('%s', %d, true)", $index, $command->number);
        }
    }

    /**
     * Compile a add partition table command.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @return string
     */
    public function compileAddPartition(Blueprint $blueprint, Fluent $command)
    {
        if ($command->type === 'list') {
            $sql = sprintf('IN (%s)', implode(',', array_map([$this, 'wrapString'], $command->conditions)));
        }

        if ($command->type === 'range') {
            $sql = sprintf("FROM ('%s') TO ('%s')", $command->conditions[0], $command->conditions[1]);
        }

        return sprintf(
            "create table  %s partition of %s FOR VALUES %s",
            $command->table,
            $this->wrapTable($blueprint),
            $sql
        );
    }

    /**
     * Convert an array of column names into a delimited string.
     *
     * @param  array   $columns
     * @return string
     */
    public function columnize(array $columns)
    {
        return implode(',', array_map(function ($column) {
            if ($column === 'deleted_at') {
                return "COALESCE({$this->wrap($column)}, '1970-01-01 08:00:00'::timestamp)";
            }

            return $this->wrap($column);
        }, $columns));
    }


    /**
     * Wrap a single string in keyword identifiers.
     *
     * @param  string  $value
     * @return string
     */
    protected function wrapString($value)
    {
        if ($value !== '*') {
            return "'".str_replace("'", "''''", $value)."'";
        }

        return $value;
    }

    /**
     * Get the SQL for an auto-increment column modifier.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $column
     * @return string|null
     */
    protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
    {
        if (in_array($column->type, $this->serials) && $column->autoIncrement) {
            if ($blueprint->partition && $blueprint->partition['column'] !==  $column->name) {
                return '';
            }

            return ' primary key';
        }
    }
}
