<?php

use App\Models\Ecommerce\Product\Brand\Brand;
use App\Models\Ecommerce\Product\Category\Category;
use App\Models\Ecommerce\Product\Collection\Collection;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;

uses(RefreshDatabase::class);

beforeEach(function () {
    Config::set('ecommerce.api.token', 'test-token');
});

function createFilterCategory(array $attributes = []): Category
{
    $name = $attributes['name'] ?? 'Test Category '.Str::random(5);
    $nameValue = is_array($name) ? $name : ['en' => $name];
    $slug = Str::slug(is_array($name) ? ($name['en'] ?? reset($name)) : $name);

    return Category::create(array_merge([
        'name' => $nameValue,
        'slug' => $slug,
        'is_visible' => true,
        'sort_order' => 1,
        'type' => 'default',
    ], $attributes));
}

function createFilterBrand(array $attributes = []): Brand
{
    $name = $attributes['name'] ?? 'Test Brand '.Str::random(5);

    return Brand::create(array_merge([
        'name' => $name,
        'slug' => Str::slug($name),
        'is_visible' => true,
        'sort_order' => 1,
    ], $attributes));
}

function createFilterCollection(array $attributes = []): Collection
{
    $name = $attributes['name'] ?? 'Test Collection '.Str::random(5);
    $nameValue = is_array($name) ? $name : ['en' => $name];
    $slug = Str::slug(is_array($name) ? ($name['en'] ?? reset($name)) : $name);

    return Collection::create(array_merge([
        'name' => $nameValue,
        'slug' => $slug,
        'is_visible' => true,
        'sort_order' => 1,
    ], $attributes));
}

function createFilterProduct(array $attributes = []): Product
{
    $name = $attributes['name'] ?? 'Test Product '.Str::random(5);

    return Product::create(array_merge([
        'name' => $name,
        'slug' => Str::slug($name),
        'is_visible' => true,
        'price_amount' => 100.00,
        'stock' => 10,
        'parent_id' => null,
    ], $attributes));
}

test('can get filters', function () {
    // Arrange
    createFilterCategory(['name' => 'Cat 1']);
    createFilterBrand(['name' => 'Brand 1']);

    // Act
    $response = $this->withHeaders([
        'Api-Token' => 'test-token',
        'Accept' => 'application/json',
    ])->getJson('/api/v1/filters');

    // Assert
    $response->assertOk()
        ->assertJsonStructure([
            'data' => [
                'categories',
                'brands',
            ],
        ]);

    $this->assertCount(1, $response->json('data.categories'));
    $this->assertCount(1, $response->json('data.brands'));
});

test('can get filters filtered by category', function () {
    // Arrange
    $parent = createFilterCategory(['name' => 'Parent']);
    $child = createFilterCategory(['name' => 'Child', 'parent_id' => $parent->id]);
    $other = createFilterCategory(['name' => 'Other']);

    $brand1 = createFilterBrand(['name' => 'Brand 1']);
    $brand2 = createFilterBrand(['name' => 'Brand 2']);

    $product = createFilterProduct(['brand_id' => $brand1->id]);
    $parent->products()->attach($product->id); // Attach to parent

    // Act
    $response = $this->withHeaders([
        'Api-Token' => 'test-token',
        'Accept' => 'application/json',
    ])->getJson("/api/v1/filters?category_id={$parent->id}");

    // Assert
    $response->assertOk();

    // Categories should be children of the requested category
    $categories = collect($response->json('data.categories'));
    $this->assertTrue($categories->contains('id', $child->id));
    $this->assertFalse($categories->contains('id', $other->id));

    // Brands should be brands of products in this category
    $brands = collect($response->json('data.brands'));
    $this->assertTrue($brands->contains('id', $brand1->id));
    $this->assertFalse($brands->contains('id', $brand2->id));
});

test('can get filters filtered by brand', function () {
    // Arrange
    $brand = createFilterBrand(['name' => 'Brand']);
    $otherBrand = createFilterBrand(['name' => 'Other Brand']);

    $category1 = createFilterCategory(['name' => 'Cat 1']);
    $category2 = createFilterCategory(['name' => 'Cat 2']);

    $product = createFilterProduct(['brand_id' => $brand->id]);
    $category1->products()->attach($product->id);

    // Act
    $response = $this->withHeaders([
        'Api-Token' => 'test-token',
        'Accept' => 'application/json',
    ])->getJson("/api/v1/filters?brand_id={$brand->id}");

    // Assert
    $response->assertOk();

    // Categories should be categories containing products of this brand
    $categories = collect($response->json('data.categories'));
    $this->assertTrue($categories->contains('id', $category1->id));
    $this->assertFalse($categories->contains('id', $category2->id));

    // Brands should be the requested brand
    $brands = collect($response->json('data.brands'));
    $this->assertTrue($brands->contains('id', $brand->id));
    $this->assertFalse($brands->contains('id', $otherBrand->id));
});

test('can get filters filtered by collection', function () {
    // Arrange
    $collection = createFilterCollection(['name' => 'Collection']);

    $brand1 = createFilterBrand(['name' => 'Brand 1']);
    $brand2 = createFilterBrand(['name' => 'Brand 2']);

    $category1 = createFilterCategory(['name' => 'Cat 1']);
    $category2 = createFilterCategory(['name' => 'Cat 2']);

    $product = createFilterProduct(['brand_id' => $brand1->id]);

    $collection->products()->attach($product->id);
    $category1->products()->attach($product->id);

    // Act
    $response = $this->withHeaders([
        'Api-Token' => 'test-token',
        'Accept' => 'application/json',
    ])->getJson("/api/v1/filters?collection_id={$collection->id}");

    // Assert
    $response->assertOk();

    // Categories should be categories of products in this collection
    $categories = collect($response->json('data.categories'));
    $this->assertTrue($categories->contains('id', $category1->id));
    $this->assertFalse($categories->contains('id', $category2->id));

    // Brands should be brands of products in this collection
    $brands = collect($response->json('data.brands'));
    $this->assertTrue($brands->contains('id', $brand1->id));
    $this->assertFalse($brands->contains('id', $brand2->id));
});
