<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Integration\Sku;

use App\Model\SkuMapping;
use App\Model\SkuOrigin;
use HyperfTest\TestCase;
use HyperfTest\Traits\AuthenticatedTestTrait;

/**
 * SkuMappingController 集成测试
 *
 * 覆盖列表筛选、origin_sku_id 过滤、创建校验（platform_product_id 可选）、CRUD、认证拦截、
 * 三元组唯一性冲突、bundled 字段 CRUD 与筛选、组合商品映射（NC-6）、origin_sku 快照自动填充（NC-7）
 *
 * @internal
 * @coversNothing
 */
class SkuMappingControllerTest extends TestCase
{
    use AuthenticatedTestTrait;

    protected function hasData(): bool
    {
        return $this->runInCoroutine(static function (): bool {
            return SkuMapping::query()->exists();
        });
    }

    protected function getFirstRecord(): ?array
    {
        return $this->runInCoroutine(static function (): ?array {
            $record = SkuMapping::query()->first();
            return $record ? $record->toArray() : null;
        });
    }

    protected function getFirstOrigin(): ?array
    {
        return $this->runInCoroutine(static function (): ?array {
            $record = SkuOrigin::query()->first();
            return $record ? $record->toArray() : null;
        });
    }

    protected function createTestOrigin(int $company_id): array
    {
        return $this->runInCoroutine(static function () use ($company_id): array {
            // bin2hex(random_bytes) 比 uniqid() 子串更可靠，避免 barcode 微秒碰撞
            $suffix = bin2hex(random_bytes(8));
            $origin = SkuOrigin::query()->create([
                'company_id' => $company_id,
                'sku' => 'TESTSKU-' . $suffix,
                'barcode' => 'BC-' . $suffix,
                'name' => 'P21.3 Test Origin ' . $suffix,
            ]);
            return $origin->toArray();
        });
    }

    protected function deleteTestOrigins(array $ids): void
    {
        if (empty($ids)) {
            return;
        }
        $this->runInCoroutine(static function () use ($ids): void {
            SkuOrigin::query()->whereIn('id', $ids)->delete();
        });
    }

    // ========== 列表接口 ==========

    public function test_list_returns_paginated_data(): void
    {
        $response = $this->get('/api/v1/sku-mappings', [], $this->authHeaders());

        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);
        $response->assertJsonStructure([
            'code',
            'message',
            'data' => [
                'items',
                'total',
                'page',
                'per_page',
            ],
        ]);
    }

    public function test_list_filter_by_origin_sku_id(): void
    {
        if (!$this->hasData()) {
            $this->markTestSkipped('没有 SKU Mapping 数据');
        }

        $first = $this->getFirstRecord();
        if (!$first['origin_sku_id']) {
            $this->markTestSkipped('首条记录无 origin_sku_id');
        }

        $response = $this->get('/api/v1/sku-mappings', [
            'origin_sku_id' => $first['origin_sku_id'],
        ], $this->authHeaders());

        $response->assertStatus(200);
        $body = json_decode($response->getContent(), true);

        foreach ($body['data']['items'] as $item) {
            $this->assertSame($first['origin_sku_id'], $item['origin_sku_id']);
        }
    }

    public function test_list_filter_by_company_and_platform(): void
    {
        if (!$this->hasData()) {
            $this->markTestSkipped('没有 SKU Mapping 数据');
        }

        $first = $this->getFirstRecord();

        $response = $this->get('/api/v1/sku-mappings', [
            'company_id' => $first['company_id'],
            'platform_id' => $first['platform_id'],
        ], $this->authHeaders());

        $response->assertStatus(200);
        $body = json_decode($response->getContent(), true);

        foreach ($body['data']['items'] as $item) {
            $this->assertSame($first['company_id'], $item['company_id']);
            $this->assertSame($first['platform_id'], $item['platform_id']);
        }
    }

    public function test_list_filter_by_enabled(): void
    {
        $response = $this->get('/api/v1/sku-mappings', ['enabled' => 'true'], $this->authHeaders());

        $response->assertStatus(200);
        $body = json_decode($response->getContent(), true);

        foreach ($body['data']['items'] as $item) {
            $this->assertTrue($item['enabled']);
        }
    }

    // ========== 创建校验 ==========

    public function test_create_without_platform_product_id_succeeds(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => $origin['company_id'],
            'platform_id' => 1,
            'origin_sku' => $origin['sku'],
            'origin_sku_id' => $origin['id'],
            'platform_outer_sku' => 'TEST-' . uniqid(),
        ], $this->authHeaders());

        $response->assertStatus(201);
        $response->assertJsonPath('code', 0);

        // 清理测试数据
        $body = json_decode($response->getContent(), true);
        if (isset($body['data']['id'])) {
            $this->runInCoroutine(static function () use ($body): void {
                SkuMapping::query()->where('id', $body['data']['id'])->delete();
            });
        }
    }

    public function test_create_with_platform_product_id_succeeds(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => $origin['company_id'],
            'platform_id' => 1,
            'origin_sku' => $origin['sku'],
            'origin_sku_id' => $origin['id'],
            'platform_product_id' => 'PROD-TEST-' . uniqid(),
            'platform_outer_sku' => 'TEST-' . uniqid(),
        ], $this->authHeaders());

        $response->assertStatus(201);
        $response->assertJsonPath('code', 0);

        // 清理测试数据
        $body = json_decode($response->getContent(), true);
        if (isset($body['data']['id'])) {
            $this->runInCoroutine(static function () use ($body): void {
                SkuMapping::query()->where('id', $body['data']['id'])->delete();
            });
        }
    }

    public function test_create_requires_company_id(): void
    {
        $response = $this->post('/api/v1/sku-mappings', [
            'platform_id' => 1,
            'origin_sku_id' => 1,
            'platform_outer_sku' => 'TEST-SKU',
        ], $this->authHeaders());

        $response->assertStatus(422);
        $response->assertJsonPath('message', '缺少必填字段: company_id');
    }

    public function test_create_requires_platform_id(): void
    {
        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => 1,
            'origin_sku_id' => 1,
            'platform_outer_sku' => 'TEST-SKU',
        ], $this->authHeaders());

        $response->assertStatus(422);
        $response->assertJsonPath('message', '缺少必填字段: platform_id');
    }

    public function test_create_requires_origin_sku_id(): void
    {
        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => 1,
            'platform_id' => 1,
            'platform_outer_sku' => 'TEST-SKU',
        ], $this->authHeaders());

        $response->assertStatus(422);
        $response->assertJsonPath('message', '缺少必填字段: origin_sku_id');
    }

    public function test_create_requires_platform_outer_sku(): void
    {
        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => 1,
            'platform_id' => 1,
            'origin_sku_id' => 1,
        ], $this->authHeaders());

        $response->assertStatus(422);
        $response->assertJsonPath('message', '缺少必填字段: platform_outer_sku');
    }

    // ========== 详情接口 ==========

    public function test_detail_not_found_returns_404(): void
    {
        $response = $this->get('/api/v1/sku-mappings/999999999', [], $this->authHeaders());

        $response->assertStatus(404);
        $response->assertJsonPath('code', 404);
    }

    // ========== 更新 & 删除 ==========

    public function test_update_mapping(): void
    {
        if (!$this->hasData()) {
            $this->markTestSkipped('没有 SKU Mapping 数据');
        }

        $first = $this->getFirstRecord();

        $response = $this->put('/api/v1/sku-mappings/' . $first['id'], [
            'note' => 'integration-test-update-' . uniqid(),
        ], $this->authHeaders());

        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);
    }

    public function test_delete_mapping(): void
    {
        // 创建一条临时记录然后删除
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $createResponse = $this->post('/api/v1/sku-mappings', [
            'company_id' => $origin['company_id'],
            'platform_id' => 1,
            'origin_sku' => $origin['sku'],
            'origin_sku_id' => $origin['id'],
            'platform_outer_sku' => 'DELETE-TEST-' . uniqid(),
        ], $this->authHeaders());

        $createResponse->assertStatus(201);
        $body = json_decode($createResponse->getContent(), true);
        $newId = $body['data']['id'];

        $deleteResponse = $this->delete('/api/v1/sku-mappings/' . $newId, [], $this->authHeaders());

        $deleteResponse->assertStatus(200);
        $deleteResponse->assertJsonPath('code', 0);
    }

    // ========== 三元组唯一性冲突（P21.3） ==========

    public function test_store_duplicate_mapping_returns_422(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $platform_outer_sku = 'DUP-TEST-' . uniqid();
        $created_id = null;

        try {
            $first_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => $platform_outer_sku,
            ], $this->authHeaders());

            $first_response->assertStatus(201);
            $first_body = json_decode($first_response->getContent(), true);
            $created_id = $first_body['data']['id'];

            $dup_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => $platform_outer_sku,
            ], $this->authHeaders());

            $dup_response->assertStatus(422);
            $dup_response->assertJsonPath(
                'message',
                '该映射组合已存在（origin_sku_id + platform_id + platform_outer_sku）',
            );
        } finally {
            if ($created_id !== null) {
                $this->runInCoroutine(static function () use ($created_id): void {
                    SkuMapping::query()->where('id', $created_id)->delete();
                });
            }
        }
    }

    public function test_update_to_duplicate_mapping_returns_422(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $suffix = uniqid();
        $a_sku = 'AAA-' . $suffix;
        $b_sku = 'BBB-' . $suffix;
        $a_id = null;
        $b_id = null;

        try {
            $a_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => $a_sku,
            ], $this->authHeaders());
            $a_response->assertStatus(201);
            $a_id = json_decode($a_response->getContent(), true)['data']['id'];

            $b_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => $b_sku,
            ], $this->authHeaders());
            $b_response->assertStatus(201);
            $b_id = json_decode($b_response->getContent(), true)['data']['id'];

            // 把 B 改成 A 的 platform_outer_sku 触发三元组冲突
            $update_response = $this->put('/api/v1/sku-mappings/' . $b_id, [
                'platform_outer_sku' => $a_sku,
            ], $this->authHeaders());

            $update_response->assertStatus(422);
            $update_response->assertJsonPath(
                'message',
                '该映射组合已存在（origin_sku_id + platform_id + platform_outer_sku）',
            );
        } finally {
            $this->runInCoroutine(static function () use ($a_id, $b_id): void {
                if ($a_id !== null) {
                    SkuMapping::query()->where('id', $a_id)->delete();
                }
                if ($b_id !== null) {
                    SkuMapping::query()->where('id', $b_id)->delete();
                }
            });
        }
    }

    // ========== bundled 字段 CRUD（P21.3） ==========

    public function test_store_with_bundled_true(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => $origin['company_id'],
            'platform_id' => 1,
            'origin_sku_id' => $origin['id'],
            'platform_outer_sku' => 'BUNDLED-T-' . uniqid(),
            'bundled' => true,
        ], $this->authHeaders());

        $response->assertStatus(201);
        $response->assertJsonPath('data.bundled', true);

        $body = json_decode($response->getContent(), true);
        $new_id = $body['data']['id'];
        $this->runInCoroutine(static function () use ($new_id): void {
            SkuMapping::query()->where('id', $new_id)->delete();
        });
    }

    public function test_store_defaults_bundled_false(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $response = $this->post('/api/v1/sku-mappings', [
            'company_id' => $origin['company_id'],
            'platform_id' => 1,
            'origin_sku_id' => $origin['id'],
            'platform_outer_sku' => 'BUNDLED-F-' . uniqid(),
        ], $this->authHeaders());

        $response->assertStatus(201);
        $response->assertJsonPath('data.bundled', false);

        $body = json_decode($response->getContent(), true);
        $new_id = $body['data']['id'];
        $this->runInCoroutine(static function () use ($new_id): void {
            SkuMapping::query()->where('id', $new_id)->delete();
        });
    }

    public function test_update_bundled_field(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $created_id = null;
        try {
            $create_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => 'BUNDLED-UP-' . uniqid(),
            ], $this->authHeaders());
            $create_response->assertStatus(201);
            $created_id = json_decode($create_response->getContent(), true)['data']['id'];

            $update_response = $this->put('/api/v1/sku-mappings/' . $created_id, [
                'bundled' => true,
            ], $this->authHeaders());
            $update_response->assertStatus(200);

            $detail_response = $this->get('/api/v1/sku-mappings/' . $created_id, [], $this->authHeaders());
            $detail_response->assertStatus(200);
            $detail_response->assertJsonPath('data.bundled', true);
        } finally {
            if ($created_id !== null) {
                $this->runInCoroutine(static function () use ($created_id): void {
                    SkuMapping::query()->where('id', $created_id)->delete();
                });
            }
        }
    }

    public function test_index_filter_by_bundled(): void
    {
        // 自建 bundled=true / false 两条记录，避免依赖既有数据量导致空 items 静默通过
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $suffix = uniqid();
        $true_id = null;
        $false_id = null;
        try {
            $true_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => 'FILTER-T-' . $suffix,
                'bundled' => true,
            ], $this->authHeaders());
            $true_response->assertStatus(201);
            $true_id = json_decode($true_response->getContent(), true)['data']['id'];

            $false_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => 'FILTER-F-' . $suffix,
                'bundled' => false,
            ], $this->authHeaders());
            $false_response->assertStatus(201);
            $false_id = json_decode($false_response->getContent(), true)['data']['id'];

            $list_true = $this->get('/api/v1/sku-mappings', ['bundled' => 'true', 'per_page' => 100], $this->authHeaders());
            $list_true->assertStatus(200);
            $body_true = json_decode($list_true->getContent(), true);
            $this->assertNotEmpty($body_true['data']['items'], 'bundled=true 筛选应至少命中新建的种子记录');
            foreach ($body_true['data']['items'] as $item) {
                $this->assertTrue($item['bundled'], "bundled=true 筛选却返回 bundled=false 的记录 id={$item['id']}");
            }

            $list_false = $this->get('/api/v1/sku-mappings', ['bundled' => 'false', 'per_page' => 100], $this->authHeaders());
            $list_false->assertStatus(200);
            $body_false = json_decode($list_false->getContent(), true);
            $this->assertNotEmpty($body_false['data']['items'], 'bundled=false 筛选应至少命中新建的种子记录');
            foreach ($body_false['data']['items'] as $item) {
                $this->assertFalse($item['bundled'], "bundled=false 筛选却返回 bundled=true 的记录 id={$item['id']}");
            }
        } finally {
            $this->runInCoroutine(static function () use ($true_id, $false_id): void {
                foreach ([$true_id, $false_id] as $id) {
                    if ($id !== null) {
                        SkuMapping::query()->where('id', $id)->delete();
                    }
                }
            });
        }
    }

    // ========== 组合商品场景（P21.3, NC-6） ==========

    public function test_bundle_multiple_origin_skus_to_same_platform_outer_sku(): void
    {
        $base_origin = $this->getFirstOrigin();
        if (!$base_origin) {
            $this->markTestSkipped('没有 SKU Origin 基线数据');
        }

        $company_id = (int) $base_origin['company_id'];
        $extra_origin_ids = [];
        $created_mapping_ids = [];

        try {
            // createTestOrigin 的任一次调用异常也能被 finally 清理已创建的 origin
            $extra_1 = $this->createTestOrigin($company_id);
            $extra_origin_ids[] = $extra_1['id'];
            $extra_2 = $this->createTestOrigin($company_id);
            $extra_origin_ids[] = $extra_2['id'];

            $origins = [$base_origin, $extra_1, $extra_2];
            $platform_outer_sku = 'BUNDLE-SHARED-' . uniqid();

            foreach ($origins as $origin) {
                $response = $this->post('/api/v1/sku-mappings', [
                    'company_id' => $origin['company_id'],
                    'platform_id' => 1,
                    'origin_sku_id' => $origin['id'],
                    'platform_outer_sku' => $platform_outer_sku,
                    'bundled' => true,
                ], $this->authHeaders());

                $response->assertStatus(201);
                $body = json_decode($response->getContent(), true);
                $this->assertSame($platform_outer_sku, $body['data']['platform_outer_sku']);
                $this->assertTrue($body['data']['bundled']);
                $created_mapping_ids[] = $body['data']['id'];
            }

            $this->assertCount(3, array_unique($created_mapping_ids));
        } finally {
            if (!empty($created_mapping_ids)) {
                $this->runInCoroutine(static function () use ($created_mapping_ids): void {
                    SkuMapping::query()->whereIn('id', $created_mapping_ids)->delete();
                });
            }
            $this->deleteTestOrigins($extra_origin_ids);
        }
    }

    public function test_bundle_mapping_unique_constraint_per_origin_sku(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $platform_outer_sku = 'BUNDLE-DUP-' . uniqid();
        $created_id = null;

        try {
            $first_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => $platform_outer_sku,
                'bundled' => true,
            ], $this->authHeaders());
            $first_response->assertStatus(201);
            $first_body = json_decode($first_response->getContent(), true);
            $created_id = $first_body['data']['id'];
            // 额外断言首次 bundled=true 被正确持久化，与 test_store_duplicate_mapping 区分
            $this->assertTrue($first_body['data']['bundled']);

            // bundled=true 不应绕过三元组唯一索引
            $dup_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'platform_outer_sku' => $platform_outer_sku,
                'bundled' => true,
            ], $this->authHeaders());

            $dup_response->assertStatus(422);
            $dup_response->assertJsonPath(
                'message',
                '该映射组合已存在（origin_sku_id + platform_id + platform_outer_sku）',
            );
        } finally {
            if ($created_id !== null) {
                $this->runInCoroutine(static function () use ($created_id): void {
                    SkuMapping::query()->where('id', $created_id)->delete();
                });
            }
        }
    }

    // ========== origin_sku 快照自动填充（P21.3, NC-7） ==========

    public function test_store_auto_fills_origin_sku_from_sku_origin(): void
    {
        $origin = $this->getFirstOrigin();
        if (!$origin) {
            $this->markTestSkipped('没有 SKU Origin 数据');
        }

        $created_id = null;
        try {
            // 客户端传入的 origin_sku 必须被服务端的快照覆盖，而非尊重请求体值
            $response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin['id'],
                'origin_sku' => 'CLIENT-SHOULD-NOT-WIN-' . uniqid(),
                'platform_outer_sku' => 'SNAPSHOT-' . uniqid(),
            ], $this->authHeaders());

            $response->assertStatus(201);
            $response->assertJsonPath('data.origin_sku', $origin['sku']);

            $created_id = json_decode($response->getContent(), true)['data']['id'];
        } finally {
            if ($created_id !== null) {
                $this->runInCoroutine(static function () use ($created_id): void {
                    SkuMapping::query()->where('id', $created_id)->delete();
                });
            }
        }
    }

    public function test_update_origin_sku_id_refreshes_origin_sku_snapshot(): void
    {
        $origin_a = $this->getFirstOrigin();
        if (!$origin_a) {
            $this->markTestSkipped('没有 SKU Origin 基线数据');
        }

        $origin_b = null;
        $mapping_id = null;
        try {
            $origin_b = $this->createTestOrigin((int) $origin_a['company_id']);

            $create_response = $this->post('/api/v1/sku-mappings', [
                'company_id' => $origin_a['company_id'],
                'platform_id' => 1,
                'origin_sku_id' => $origin_a['id'],
                'platform_outer_sku' => 'SNAPSHOT-UP-' . uniqid(),
            ], $this->authHeaders());
            $create_response->assertStatus(201);
            $create_body = json_decode($create_response->getContent(), true);
            $mapping_id = $create_body['data']['id'];
            $this->assertSame($origin_a['sku'], $create_body['data']['origin_sku']);

            $update_response = $this->put('/api/v1/sku-mappings/' . $mapping_id, [
                'origin_sku_id' => $origin_b['id'],
            ], $this->authHeaders());

            $update_response->assertStatus(200);
            $update_response->assertJsonPath('data.origin_sku', $origin_b['sku']);
            $update_response->assertJsonPath('data.origin_sku_id', $origin_b['id']);
        } finally {
            if ($mapping_id !== null) {
                $this->runInCoroutine(static function () use ($mapping_id): void {
                    SkuMapping::query()->where('id', $mapping_id)->delete();
                });
            }
            if ($origin_b !== null) {
                $this->deleteTestOrigins([$origin_b['id']]);
            }
        }
    }

    // ========== 认证拦截 ==========

    public function test_list_without_token_returns_401(): void
    {
        $response = $this->get('/api/v1/sku-mappings');

        $response->assertStatus(401);
    }
}
