<?php

declare(strict_types=1);

namespace Doctrine\Tests\Common\Collections;

use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\Common\Collections\Expr\CompositeExpression;
use Doctrine\Common\Collections\ExpressionBuilder;
use Doctrine\Common\Collections\Order;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
use PHPUnit\Framework\TestCase;
use SortDirection;

class CriteriaTest extends TestCase
{
    use VerifyDeprecations;

    #[IgnoreDeprecations]
    public function testCreateLegacy(): void
    {
        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/collections/pull/486');

        $criteria = Criteria::create(true);

        self::assertInstanceOf(Criteria::class, $criteria);
    }

    #[IgnoreDeprecations]
    public function testConstructorLegacy(): void
    {
        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/collections/pull/486');

        new Criteria(new Comparison('test', '=', 'test'), null, 0, null, true);
    }

    public function testCreate(): void
    {
        $criteria = Criteria::create();

        self::assertInstanceOf(Criteria::class, $criteria);
    }

    #[IgnoreDeprecations]
    public function testConstructor(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = new Criteria($expr, ['foo' => Order::Ascending], 10, 20);

        self::assertSame($expr, $criteria->getWhereExpression());
        self::assertSame(['foo' => Order::Ascending], $criteria->orderings());
        self::assertSame(10, $criteria->getFirstResult());
        self::assertSame(20, $criteria->getMaxResults());
    }

    public function testWhere(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = Criteria::create();

        $criteria->where($expr);

        self::assertSame($expr, $criteria->getWhereExpression());
    }

    public function testAndWhere(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = Criteria::create();

        $criteria->where($expr);
        $expr = $criteria->getWhereExpression();
        $criteria->andWhere($expr);

        $where = $criteria->getWhereExpression();
        self::assertInstanceOf(CompositeExpression::class, $where);

        self::assertEquals(CompositeExpression::TYPE_AND, $where->getType());
        self::assertSame([$expr, $expr], $where->getExpressionList());
    }

    public function testAndWhereWithoutWhere(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = Criteria::create();

        $criteria->andWhere($expr);

        self::assertSame($expr, $criteria->getWhereExpression());
    }

    public function testOrWhere(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = Criteria::create();

        $criteria->where($expr);
        $expr = $criteria->getWhereExpression();
        $criteria->orWhere($expr);

        $where = $criteria->getWhereExpression();
        self::assertInstanceOf(CompositeExpression::class, $where);

        self::assertEquals(CompositeExpression::TYPE_OR, $where->getType());
        self::assertSame([$expr, $expr], $where->getExpressionList());
    }

    public function testOrWhereWithoutWhere(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = Criteria::create();

        $criteria->orWhere($expr);

        self::assertSame($expr, $criteria->getWhereExpression());
    }

    #[IgnoreDeprecations]
    public function testOrderings(): void
    {
        $criteria = Criteria::create()
            ->orderBy(['foo' => SortDirection::Ascending]);

        self::assertEquals(['foo' => Order::Ascending], $criteria->orderings());
    }

    public function testExpr(): void
    {
        self::assertInstanceOf(ExpressionBuilder::class, Criteria::expr());
    }

    public function testGetOrderings(): void
    {
        $criteria = Criteria::create()
            ->orderBy(['foo' => SortDirection::Ascending, 'bar' => Order::Descending]);

        self::assertEquals([
            'foo' => SortDirection::Ascending,
            'bar' => SortDirection::Descending,
        ], $criteria->getOrderings());
    }

    #[IgnoreDeprecations]
    public function testOrderingsWithMixedOrderTypes(): void
    {
        $criteria = Criteria::create()
            ->orderBy([
                'foo' => SortDirection::Ascending,
                'bar' => Order::Descending,
                'baz' => SortDirection::Descending,
            ]);

        $orderings = $criteria->orderings();

        self::assertCount(3, $orderings);
        self::assertEquals(Order::Ascending, $orderings['foo']);
        self::assertEquals(Order::Descending, $orderings['bar']);
        self::assertEquals(Order::Descending, $orderings['baz']);
    }

    #[IgnoreDeprecations]
    public function testConstructorWithMixedOrderTypes(): void
    {
        $expr     = new Comparison('field', '=', 'value');
        $criteria = new Criteria($expr, [
            'foo' => Order::Ascending,
            'bar' => SortDirection::Descending,
        ], 5, 15);

        self::assertSame($expr, $criteria->getWhereExpression());

        $getOrderings = $criteria->getOrderings();
        self::assertEquals(SortDirection::Ascending, $getOrderings['foo']);
        self::assertEquals(SortDirection::Descending, $getOrderings['bar']);

        $orderings = $criteria->orderings();
        self::assertEquals(Order::Ascending, $orderings['foo']);
        self::assertEquals(Order::Descending, $orderings['bar']);

        self::assertSame(5, $criteria->getFirstResult());
        self::assertSame(15, $criteria->getMaxResults());
    }
}
