<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Unit\Controller;

use App\Controller\AbstractDataController;
use App\Model\Product;
use Hyperf\Database\Model\Builder;
use Hyperf\HttpServer\Contract\RequestInterface;
use PHPUnit\Framework\TestCase;

/**
 * AbstractDataController 单元测试
 *
 * 测试 applyFilters 各筛选模式生成的 SQL 条件
 *
 * @internal
 * @coversNothing
 */
class AbstractDataControllerTest extends TestCase
{
    /**
     * 创建一个可测试的 AbstractDataController 具体子类实例
     */
    protected function createController(array $filters, array $request_params = []): AbstractDataController
    {
        $mock_request = $this->createMock(RequestInterface::class);
        $mock_request->method('input')
            ->willReturnCallback(function (string $key, $default = null) use ($request_params) {
                return $request_params[$key] ?? $default;
            });
        $mock_request->method('getAttribute')
            ->willReturnCallback(function (string $key, $default = null) {
                if ($key === 'scope_type') {
                    return 'all';
                }
                return $default;
            });

        // 创建匿名具体子类
        $controller = new class ($filters) extends AbstractDataController {
            private array $testFilters;

            public function __construct(array $filters)
            {
                $this->testFilters = $filters;
            }

            protected function getModelClass(): string
            {
                return Product::class;
            }

            protected function getListFields(): array
            {
                return ['*'];
            }

            protected function getDetailFields(): array
            {
                return ['*'];
            }

            protected function getAllowedFilters(): array
            {
                return $this->testFilters;
            }

            /**
             * 暴露 applyFilters 用于测试
             */
            public function testApplyFilters(Builder $query): void
            {
                $this->applyFilters($query);
            }

            /**
             * 暴露 applyDataScope 用于测试
             */
            public function testApplyDataScope(Builder $query): void
            {
                $this->applyDataScope($query);
            }
        };

        // 通过反射注入 mock request
        $reflection = new \ReflectionClass(AbstractDataController::class);
        $parent = $reflection->getParentClass();
        $prop = $parent->getProperty('request');
        $prop->setAccessible(true);
        $prop->setValue($controller, $mock_request);

        return $controller;
    }

    /**
     * 创建一个带 scope 属性的 controller
     */
    protected function createControllerWithScope(string $scope_type, array $scope_ids): AbstractDataController
    {
        $mock_request = $this->createMock(RequestInterface::class);
        $mock_request->method('input')
            ->willReturn(null);
        $mock_request->method('getAttribute')
            ->willReturnCallback(function (string $key, $default = null) use ($scope_type, $scope_ids) {
                if ($key === 'scope_type') {
                    return $scope_type;
                }
                if ($key === 'scope_ids') {
                    return $scope_ids;
                }
                return $default;
            });

        $controller = new class () extends AbstractDataController {
            public function __construct() {}

            protected function getModelClass(): string
            {
                return Product::class;
            }

            protected function getListFields(): array
            {
                return ['*'];
            }

            protected function getDetailFields(): array
            {
                return ['*'];
            }

            protected function getAllowedFilters(): array
            {
                return [];
            }

            public function testApplyDataScope(Builder $query): void
            {
                $this->applyDataScope($query);
            }
        };

        $reflection = new \ReflectionClass(AbstractDataController::class);
        $parent = $reflection->getParentClass();
        $prop = $parent->getProperty('request');
        $prop->setAccessible(true);
        $prop->setValue($controller, $mock_request);

        return $controller;
    }

    /**
     * 从 Builder 提取 where 条件，返回可断言的结构
     */
    protected function extractWheres(Builder $query): array
    {
        return $query->getQuery()->wheres;
    }

    protected function createBuilder(): Builder
    {
        // 使用 Product 模型创建一个干净的 Builder（不会实际执行查询）
        return Product::query()->newQuery();
    }

    // ========== exact 筛选 ==========

    public function test_apply_filters_exact_match(): void
    {
        $controller = $this->createController(
            ['company_id' => 'exact'],
            ['company_id' => '100']
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres);
        $this->assertSame('company_id', $wheres[0]['column']);
        $this->assertSame('=', $wheres[0]['operator']);
        $this->assertSame('100', $wheres[0]['value']);
    }

    public function test_apply_filters_exact_skips_null_value(): void
    {
        $controller = $this->createController(
            ['company_id' => 'exact'],
            [] // 无参数
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(0, $wheres);
    }

    public function test_apply_filters_exact_skips_empty_string(): void
    {
        $controller = $this->createController(
            ['company_id' => 'exact'],
            ['company_id' => '']
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(0, $wheres);
    }

    // ========== like 筛选 ==========

    public function test_apply_filters_like_match(): void
    {
        $controller = $this->createController(
            ['name' => 'like'],
            ['name' => '手机']
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres);
        $this->assertSame('name', $wheres[0]['column']);
        $this->assertSame('ilike', $wheres[0]['operator']);
        $this->assertSame('%手机%', $wheres[0]['value']);
    }

    // ========== date_from 筛选 ==========

    public function test_apply_filters_date_from(): void
    {
        $controller = $this->createController(
            ['created_date_from' => 'date_from'],
            ['created_date_from' => '2025-01-01']
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres);
        $this->assertSame('created_date', $wheres[0]['column']);
        $this->assertSame('>=', $wheres[0]['operator']);
        $this->assertSame('2025-01-01', $wheres[0]['value']);
    }

    // ========== date_to 筛选 ==========

    public function test_apply_filters_date_to(): void
    {
        $controller = $this->createController(
            ['created_date_to' => 'date_to'],
            ['created_date_to' => '2025-12-31']
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres);
        $this->assertSame('created_date', $wheres[0]['column']);
        $this->assertSame('<=', $wheres[0]['operator']);
        $this->assertSame('2025-12-31 23:59:59', $wheres[0]['value']);
    }

    // ========== 多条件组合 ==========

    public function test_apply_filters_multiple_conditions(): void
    {
        $controller = $this->createController(
            [
                'company_id' => 'exact',
                'name' => 'like',
                'status_id' => 'exact',
            ],
            [
                'company_id' => '1',
                'name' => 'iPhone',
                'status_id' => '2',
            ]
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(3, $wheres);
    }

    public function test_apply_filters_mixed_with_empty_values(): void
    {
        $controller = $this->createController(
            [
                'company_id' => 'exact',
                'name' => 'like',
                'status_id' => 'exact',
            ],
            [
                'company_id' => '1',
                'name' => '', // 空值应跳过
                // status_id 缺失也应跳过
            ]
        );

        $query = $this->createBuilder();
        $controller->testApplyFilters($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres); // 仅 company_id
    }

    // ========== DataScope 过滤 ==========

    public function test_apply_data_scope_all_adds_no_conditions(): void
    {
        $controller = $this->createControllerWithScope('all', []);

        $query = $this->createBuilder();
        $controller->testApplyDataScope($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(0, $wheres);
    }

    public function test_apply_data_scope_store_adds_store_id_where_in(): void
    {
        $controller = $this->createControllerWithScope('store', [10, 20, 30]);

        $query = $this->createBuilder();
        $controller->testApplyDataScope($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres);
        $this->assertSame('store_id', $wheres[0]['column']);
        $this->assertSame('In', $wheres[0]['type']);
        $this->assertSame([10, 20, 30], $wheres[0]['values']);
    }

    public function test_apply_data_scope_platform_adds_platform_id_where_in(): void
    {
        $controller = $this->createControllerWithScope('platform', [1, 2]);

        $query = $this->createBuilder();
        $controller->testApplyDataScope($query);

        $wheres = $this->extractWheres($query);
        $this->assertCount(1, $wheres);
        $this->assertSame('platform_id', $wheres[0]['column']);
        $this->assertSame('In', $wheres[0]['type']);
        $this->assertSame([1, 2], $wheres[0]['values']);
    }
}
