<?php
declare(strict_types=1);
namespace JKingWeb\Arsse\Test\Db;
use JKingWeb\Arsse\Db\Statement;

trait BindingTests {
    function testBindNull() {
        $input = null;
        $exp = [
            "null"      => null,
            "integer"   => null,
            "float"     => null,
            "date"      => null,
            "time"      => null,
            "datetime"  => null,
            "binary"    => null,
            "string"    => null,
            "boolean"   => null,
        ];
        $this->checkBinding($input, $exp);
        // types may also be strict (e.g. "strict integer") and never pass null to the database; this is useful for NOT NULL columns
        // only null input should yield different results, so only this test has different expectations
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
            "binary"    => "",
            "string"    => "",
            "boolean"   => 0,
        ];
        $this->checkBinding($input, $exp, true);
    }

    function testBindTrue() {
        $input = true;
        $exp = [
            "null"      => null,
            "integer"   => 1,
            "float"     => 1.0,
            "date"      => null,
            "time"      => null,
            "datetime"  => null,
            "binary"    => "1",
            "string"    => "1",
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindFalse() {
        $input = false;
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => null,
            "time"      => null,
            "datetime"  => null,
            "binary"    => "",
            "string"    => "",
            "boolean"   => 0,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindInteger() {
        $input = 2112;
        $exp = [
            "null"      => null,
            "integer"   => 2112,
            "float"     => 2112.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
            "binary"    => "2112",
            "string"    => "2112",
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindIntegerZero() {
        $input = 0;
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
            "binary"    => "0",
            "string"    => "0",
            "boolean"   => 0,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindFloat() {
        $input = 2112.0;
        $exp = [
            "null"      => null,
            "integer"   => 2112,
            "float"     => 2112.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
            "binary"    => "2112",
            "string"    => "2112",
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindFloatZero() {
        $input = 0.0;
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
            "binary"    => "0",
            "string"    => "0",
            "boolean"   => 0,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindAsciiString() {
        $input = "Random string";
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => null,
            "time"      => null,
            "datetime"  => null,
            "binary"    => $input,
            "string"    => $input,
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindUtf8String() {
        $input = "é";
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => null,
            "time"      => null,
            "datetime"  => null,
            "binary"    => $input,
            "string"    => $input,
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindBinaryString() {
        // FIXME: This test may be unreliable; SQLite happily stores invalid UTF-8 text as bytes untouched, but other engines probably don't do this
        $input = chr(233);
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => null,
            "time"      => null,
            "datetime"  => null,
            "binary"    => $input,
            "string"    => $input,
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindIso8601DateString() {
        $input = "2017-01-09T13:11:17";
        $time = strtotime($input);
        $exp = [
            "null"      => null,
            "integer"   => 2017,
            "float"     => 2017.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
            "binary"    => $input,
            "string"    => $input,
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindArbitraryDateString() {
        $input = "Today";
        $time = strtotime($input);
        $exp = [
            "null"      => null,
            "integer"   => 0,
            "float"     => 0.0,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
            "binary"    => $input,
            "string"    => $input,
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindMutableDateObject($class = '\DateTime') {
        $input = new $class("Noon Today");
        $time = $input->getTimestamp();
        $exp = [
            "null"      => null,
            "integer"   => $time,
            "float"     => (float) $time,
            "date"      => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
            "time"      => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
            "datetime"  => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
            "binary"    => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
            "string"    => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
            "boolean"   => 1,
        ];
        $this->checkBinding($input, $exp);
        $this->checkBinding($input, $exp, true);
    }

    function testBindImmutableDateObject() {
        $this->testBindMutableDateObject('\DateTimeImmutable');
    }
}