<?php

namespace Tests;

use App\Facades\Country;
use App\Facades\TaxonomyMapper;
use App\Http\Middleware\CurrentTenantMiddleware;
use App\Http\Middleware\ImpersonateSanctum;
use App\SiteRepository;
use App\Tier;
use Database\Seeders\Global\CountrySeeder;
use Mtc\MercuryDataModels\Tenant;
use Spatie\Permission\Middlewares\PermissionMiddleware;
use Spatie\Permission\Middlewares\RoleMiddleware;
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;

/**
 * Handles tenant initialization for tests.
 * Real tenant (from master DB) or fake tenant (no master DB needed).
 */
trait UsesTenancy
{
    // Auto-configured based on needsMasterDatabase
    protected bool $useRealTenant = true;

    // Fake tenant configuration (when useRealTenant = false)
    protected string $fakeTenantTier = Tier::ENTERPRISE->value;
    protected string $fakeTenantName = 'foo faz';
    protected string $fakeTenantDomain = 'foo.localhost';
    protected array $fakeTenantFeatures = [];

    // Optional settings for tenant-only tests
    protected bool $useTestSettingRepository = true;
    protected bool $mockCountryFacade = false;

    protected function initializeTenancy(): void
    {
        if ($this->useRealTenant) {
            $this->initializeRealTenant();
        } else {
            $this->initializeFakeTenant();
            $this->disableTenantOnlyMiddlewares();
            $this->setupTenantOnlyMocks();
        }
    }

    protected function initializeRealTenant(): void
    {
        $tenant = Tenant::create([
            'tier' => Tier::LITE->value,
        ]);
        $tenant->domains()->create([
            'domain' => 'test.localhost',
            'verified' => true,
            'primary' => true,
        ]);
        tenancy()->initialize($tenant);
    }

    protected function initializeFakeTenant(): void
    {
        $fakeTenant = new Tenant([
            'tier' => $this->fakeTenantTier,
            'name' => $this->fakeTenantName,
        ]);
        $fakeTenant->id = 'test-tenant';
        $fakeTenant->features = collect($this->fakeTenantFeatures)
            ->map(fn($f) => (object)['feature' => $f]);
        $fakeTenant->primaryDomain = (object)['domain' => $this->fakeTenantDomain];

        tenancy()->initialize($fakeTenant);

        // Mock SiteRepository for fake tenant
        if (method_exists($this, 'partialMock')) {
            $this->partialMock(SiteRepository::class, function ($mock) {
                $mock->shouldReceive('baseUrl')
                    ->andReturn("http://{$this->fakeTenantDomain}");
            });
        }
    }

    protected function disableTenantOnlyMiddlewares(): void
    {
        $this->withoutMiddleware([
            'auth:sanctum',
            InitializeTenancyByRequestData::class,
            PermissionMiddleware::class,
            RoleMiddleware::class,
            InitializeTenancyByPath::class,
            ImpersonateSanctum::class,
            CurrentTenantMiddleware::class,
        ]);
    }

    protected function setupTenantOnlyMocks(): void
    {
        if ($this->useTestSettingRepository) {
            $this->app->singleton('site-settings', TestSettingRepository::class);
        }

        $this->app->singleton('feature-flags', TestFeatureRepository::class);

        TaxonomyMapper::shouldReceive('getMappedTaxonomy')->andReturn(null);
        TaxonomyMapper::shouldReceive('storeUnmappedTaxonomies')->andReturn(false);

        if ($this->mockCountryFacade) {
            $this->setupCountryMock();
        }
    }

    protected function setupCountryMock(): void
    {
        $fakeCountries = collect((new CountrySeeder())->getCountryData())
            ->map(fn($country) => (object)[
                'name' => $country['name'],
                'code' => $country['code'],
                'has_postcodes' => $country['has_postcodes'],
            ]);

        Country::shouldReceive('getAll')
            ->andReturn($fakeCountries->map(fn($c) => (object)[
                'name' => $c->name,
                'code' => $c->code,
            ]));

        Country::shouldReceive('requiresPostcode')
            ->andReturnUsing(fn($code) => (bool)($fakeCountries->firstWhere('code', $code)?->has_postcodes));
    }
}
