<?php declare(strict_types=1);

namespace Test\Unit;

use BitBadger\PDODocument\Field;
use BitBadger\PDODocument\Op;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

/**
 * Unit tests for the Field class
 */
class FieldTest extends TestCase
{
    #[TestDox('Append parameter succeeds for EX')]
    public function testAppendParameterSucceedsForEX(): void
    {
        $this->assertEquals([], Field::EX('exists')->appendParameter([]), 'EX should not have appended a parameter');
    }

    #[TestDox('Append parameter succeeds for NEX')]
    public function testAppendParameterSucceedsForNEX(): void
    {
        $this->assertEquals([], Field::NEX('absent')->appendParameter([]), 'NEX should not have appended a parameter');
    }

    #[TestDox('Append parameter succeeds for BT')]
    public function testAppendParameterSucceedsForBT(): void
    {
        $this->assertEquals(['@nummin' => 5, '@nummax' => 9], Field::BT('exists', 5, 9, '@num')->appendParameter([]),
            'BT should have appended min and max parameters');
    }

    public function testAppendParameterSucceedsForOthers(): void
    {
        $this->assertEquals(['@test' => 33], Field::EQ('the_field', 33, '@test')->appendParameter([]),
            'Field parameter not returned correctly');
    }

    #[TestDox('To where succeeds for EX without qualifier')]
    public function testToWhereSucceedsForEXWithoutQualifier(): void
    {
        $this->assertEquals("data->>'that_field' IS NOT NULL", Field::EX('that_field')->toWhere(),
            'WHERE fragment not generated correctly');
    }

    #[TestDox('To where succeeds for NEX without qualifier')]
    public function testToWhereSucceedsForNEXWithoutQualifier(): void
    {
        $this->assertEquals("data->>'a_field' IS NULL", Field::NEX('a_field')->toWhere(),
            'WHERE fragment not generated correctly');
    }

    #[TestDox('To where succeeds for BT without qualifier')]
    public function testToWhereSucceedsForBTWithoutQualifier(): void
    {
        $this->assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field::BT('age', 13, 17, '@age')->toWhere(),
            'WHERE fragment not generated correctly');
    }

    public function testToWhereSucceedsForOthersWithoutQualifier(): void
    {
        $this->assertEquals("data->>'some_field' = @value", Field::EQ('some_field', '', '@value')->toWhere(),
            'WHERE fragment not generated correctly');
    }

    public function testToWhereSucceedsWithQualifierNoParameter(): void
    {
        $field = Field::EX('no_field');
        $field->qualifier = 'test';
        $this->assertEquals("test.data->>'no_field' IS NOT NULL", $field->toWhere(),
            'WHERE fragment not generated correctly');
    }

    public function testToWhereSucceedsWithQualifierAndParameter(): void
    {
        $field = Field::LE('le_field', 18, '@it');
        $field->qualifier = 'q';
        $this->assertEquals("q.data->>'le_field' <= @it", $field->toWhere(), 'WHERE fragment not generated correctly');
    }

    #[TestDox('EQ succeeds without parameter')]
    public function testEQSucceedsWithoutParameter(): void
    {
        $field = Field::EQ('my_test', 9);
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('my_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::EQ, $field->op, 'Operation not filled correctly');
        $this->assertEquals(9, $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('EQ succeeds with parameter')]
    public function testEQSucceedsWithParameter(): void
    {
        $field = Field::EQ('another_test', 'turkey', '@test');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('another_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::EQ, $field->op, 'Operation not filled correctly');
        $this->assertEquals('turkey', $field->value, 'Value not filled correctly');
        $this->assertEquals('@test', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('GT succeeds without parameter')]
    public function testGTSucceedsWithoutParameter(): void
    {
        $field = Field::GT('your_test', 4);
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('your_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::GT, $field->op, 'Operation not filled correctly');
        $this->assertEquals(4, $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('GT succeeds with parameter')]
    public function testGTSucceedsWithParameter(): void
    {
        $field = Field::GT('more_test', 'chicken', '@value');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('more_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::GT, $field->op, 'Operation not filled correctly');
        $this->assertEquals('chicken', $field->value, 'Value not filled correctly');
        $this->assertEquals('@value', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('GE succeeds without parameter')]
    public function testGESucceedsWithoutParameter(): void
    {
        $field = Field::GE('their_test', 6);
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('their_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::GE, $field->op, 'Operation not filled correctly');
        $this->assertEquals(6, $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('GE succeeds with parameter')]
    public function testGESucceedsWithParameter(): void
    {
        $field = Field::GE('greater_test', 'poultry', '@cluck');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('greater_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::GE, $field->op, 'Operation not filled correctly');
        $this->assertEquals('poultry', $field->value, 'Value not filled correctly');
        $this->assertEquals('@cluck', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('LT succeeds without parameter')]
    public function testLTSucceedsWithoutParameter(): void
    {
        $field = Field::LT('z', 32);
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('z', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::LT, $field->op, 'Operation not filled correctly');
        $this->assertEquals(32, $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('LT succeeds with parameter')]
    public function testLTSucceedsWithParameter(): void
    {
        $field = Field::LT('additional_test', 'fowl', '@boo');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('additional_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::LT, $field->op, 'Operation not filled correctly');
        $this->assertEquals('fowl', $field->value, 'Value not filled correctly');
        $this->assertEquals('@boo', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('LE succeeds without parameter')]
    public function testLESucceedsWithoutParameter(): void
    {
        $field = Field::LE('g', 87);
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('g', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::LE, $field->op, 'Operation not filled correctly');
        $this->assertEquals(87, $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('LE succeeds with parameter')]
    public function testLESucceedsWithParameter(): void
    {
        $field = Field::LE('lesser_test', 'hen', '@woo');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('lesser_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::LE, $field->op, 'Operation not filled correctly');
        $this->assertEquals('hen', $field->value, 'Value not filled correctly');
        $this->assertEquals('@woo', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('NE succeeds without parameter')]
    public function testNESucceedsWithoutParameter(): void
    {
        $field = Field::NE('j', 65);
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('j', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::NE, $field->op, 'Operation not filled correctly');
        $this->assertEquals(65, $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('NE succeeds with parameter')]
    public function testNESucceedsWithParameter(): void
    {
        $field = Field::NE('unequal_test', 'egg', '@zoo');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('unequal_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::NE, $field->op, 'Operation not filled correctly');
        $this->assertEquals('egg', $field->value, 'Value not filled correctly');
        $this->assertEquals('@zoo', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('BT succeeds without parameter')]
    public function testBTSucceedsWithoutParameter(): void
    {
        $field = Field::BT('k', 'alpha', 'zed');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('k', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::BT, $field->op, 'Operation not filled correctly');
        $this->assertEquals(['alpha', 'zed'], $field->value, 'Value not filled correctly');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('BT succeeds with parameter')]
    public function testBTSucceedsWithParameter(): void
    {
        $field = Field::BT('between_test', 18, 49, '@count');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('between_test', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::BT, $field->op, 'Operation not filled correctly');
        $this->assertEquals([18, 49], $field->value, 'Value not filled correctly');
        $this->assertEquals('@count', $field->paramName, 'Parameter name not filled correctly');
    }

    #[TestDox('EX succeeds')]
    public function testEXSucceeds(): void
    {
        $field = Field::EX('be_there');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('be_there', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::EX, $field->op, 'Operation not filled correctly');
        $this->assertEquals('', $field->value, 'Value should have been blank');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }

    #[TestDox('NEX succeeds')]
    public function testNEXSucceeds(): void
    {
        $field = Field::NEX('be_absent');
        $this->assertNotNull($field, 'The field should not have been null');
        $this->assertEquals('be_absent', $field->fieldName, 'Field name not filled correctly');
        $this->assertEquals(Op::NEX, $field->op, 'Operation not filled correctly');
        $this->assertEquals('', $field->value, 'Value should have been blank');
        $this->assertEquals('', $field->paramName, 'Parameter name should have been blank');
    }
}