<?php

namespace Tests\Tenant;

use App\Facades\Feature;
use App\Facades\Settings;
use Illuminate\Support\Facades\Storage;
use Mtc\MercuryDataModels\Franchise;
use Mtc\MercuryDataModels\OfferType;
use Mtc\MercuryDataModels\VehicleOffer;
use Tests\TenantTestCase;

class MakeSitemapOfferTest extends TenantTestCase
{
    private string $sitemapPath;

    protected function setUp(): void
    {
        parent::setUp();

        Storage::fake('file-storage');

        Feature::setEnabled(tenant(), ['sitemap', 'new-vehicles']);

        Settings::make([
            'tab' => 'Site',
            'section' => 'SEO',
            'group' => 'Sitemap',
            'name' => 'Offer page URL path',
            'config_key' => 'offers-url-path',
            'type' => 'string',
            'value' => '/offers/{{SLUG}}',
        ]);

        $this->sitemapPath = tenant('id') . '/sitemap.xml';
    }

    public function testDefaultSettingsGenerateTopLevelListingAndTypeListings(): void
    {
        $franchise = Franchise::factory()->create(['slug' => 'ford']);
        $offerType = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);

        VehicleOffer::factory()->create([
            'slug' => 'ford-puma-offer',
            'franchise_id' => $franchise->id,
            'type_id' => $offerType->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // Top-level listing should be present (default true)
        $this->assertStringContains('/offers', $xml);
        // Top-level type listing should be present (default true)
        $this->assertStringContains('/offers/motability', $xml);
        // Franchise listing should NOT be present (default false)
        $this->assertStringNotContains('/ford/offers</loc>', $xml);
        // Franchise type listing should NOT be present (default false)
        $this->assertStringNotContains('/ford/motability', $xml);
    }

    public function testTopLevelListingDisabled(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-top-level-listing',
            false
        );

        $franchise = Franchise::factory()->create(['slug' => 'ford']);
        VehicleOffer::factory()->create([
            'slug' => 'ford-puma-offer',
            'franchise_id' => $franchise->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringNotContains(
            'http://foo.localhost/offers',
            $xml
        );
    }

    public function testFranchiseListingEnabled(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-listing',
            true
        );

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        $volvo = Franchise::factory()->create(['slug' => 'volvo']);

        VehicleOffer::factory()->create([
            'slug' => 'ford-offer-1',
            'franchise_id' => $ford->id,
            'published' => true,
        ]);
        VehicleOffer::factory()->create([
            'slug' => 'volvo-offer-1',
            'franchise_id' => $volvo->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringContains('/ford/offers', $xml);
        $this->assertStringContains('/volvo/offers', $xml);
    }

    public function testFranchiseListingSkipsFranchiseWithNoActiveOffers(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-listing',
            true
        );

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        $volvo = Franchise::factory()->create(['slug' => 'volvo']);

        VehicleOffer::factory()->create([
            'slug' => 'ford-offer-1',
            'franchise_id' => $ford->id,
            'published' => true,
        ]);
        // Volvo has no active offers (unpublished)
        VehicleOffer::factory()->create([
            'slug' => 'volvo-offer-1',
            'franchise_id' => $volvo->id,
            'published' => false,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringContains('/ford/offers', $xml);
        $this->assertStringNotContains('/volvo/offers', $xml);
    }

    public function testFranchiseTypeListingEnabled(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-type-listing',
            true
        );

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        $motability = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);
        $fleet = OfferType::factory()->create([
            'name' => 'Fleet Business',
            'slug' => 'fleet-business',
        ]);

        VehicleOffer::factory()->create([
            'slug' => 'ford-motability-1',
            'franchise_id' => $ford->id,
            'type_id' => $motability->id,
            'published' => true,
        ]);
        VehicleOffer::factory()->create([
            'slug' => 'ford-fleet-1',
            'franchise_id' => $ford->id,
            'type_id' => $fleet->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringContains('/ford/motability', $xml);
        $this->assertStringContains('/ford/fleet-business', $xml);
    }

    public function testFranchiseTypeListingSkipsEmptyCombinations(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-type-listing',
            true
        );

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        $volvo = Franchise::factory()->create(['slug' => 'volvo']);
        $motability = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);

        // Only Ford has motability offers
        VehicleOffer::factory()->create([
            'slug' => 'ford-motability-1',
            'franchise_id' => $ford->id,
            'type_id' => $motability->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringContains('/ford/motability', $xml);
        $this->assertStringNotContains('/volvo/motability', $xml);
    }

    public function testTopLevelTypeListingDisabled(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-top-level-type-listing',
            false
        );

        $offerType = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);
        VehicleOffer::factory()->create([
            'slug' => 'motability-offer-1',
            'type_id' => $offerType->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringNotContains('/offers/motability</loc>', $xml);
    }

    public function testListingBasePathIsIndependentOfOfferDetailPath(): void
    {
        // Detail pages live at /offer/{slug} (singular)…
        Settings::update('offers-url-path', '/offer/{{SLUG}}');
        // …while listings live at /offers (plural) via the dedicated setting.
        $this->setSitemapSetting('seo-sitemap-offers-listing-path', '/offers');

        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-listing',
            true
        );

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        VehicleOffer::factory()->create([
            'slug' => 'ford-offer-1',
            'franchise_id' => $ford->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // Individual offer keeps singular /offer/{slug}
        $this->assertStringContains('/offer/ford-offer-1', $xml);
        // Top-level listing uses plural /offers
        $this->assertStringContains('http://foo.localhost/offers', $xml);
        // Franchise listing uses plural /ford/offers
        $this->assertStringContains('/ford/offers', $xml);
        // …and not the singular form derived from offers-url-path
        $this->assertStringNotContains('/ford/offer</loc>', $xml);
    }

    public function testListingBasePathDefaultsToOffersWhenSettingMissing(): void
    {
        // No seo-sitemap-offers-listing-path setting seeded — default applies.
        Settings::update('offers-url-path', '/{{TYPE}}/{{SLUG}}');

        VehicleOffer::factory()->create([
            'slug' => 'some-offer',
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringContains(
            'http://foo.localhost/offers',
            $xml
        );
    }

    public function testIndividualOfferUrlWithTypePlaceholder(): void
    {
        Settings::update('offers-url-path', '/{{TYPE}}/{{SLUG}}');

        $franchise = Franchise::factory()->create(['slug' => 'volvo']);
        $motability = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);

        VehicleOffer::factory()->create([
            'slug' => 'xc40-plus-motab',
            'franchise_id' => $franchise->id,
            'type_id' => $motability->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // Individual offer: /{franchise}/{type}/{slug}
        $this->assertStringContains(
            '/volvo/motability/xc40-plus-motab',
            $xml
        );
    }

    public function testPaginationUrlsGeneratedForListings(): void
    {
        Settings::make([
            'tab' => 'Site',
            'section' => 'General',
            'group' => 'Offers',
            'name' => 'Offers per page',
            'config_key' => 'filter-offers-per-page',
            'type' => 'int',
            'value' => 2,
        ]);

        $franchise = Franchise::factory()->create(['slug' => 'ford']);
        VehicleOffer::factory()->count(5)->create([
            'franchise_id' => $franchise->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // With 5 offers and 2 per page, pages 2 and 3 should exist
        $this->assertStringContains('/offers?page=2', $xml);
        $this->assertStringContains('/offers?page=3', $xml);
    }

    public function testAllListingTypesEnabledTogether(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-top-level-listing',
            true
        );
        $this->setSitemapSetting(
            'seo-sitemap-offers-top-level-type-listing',
            true
        );
        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-listing',
            true
        );
        $this->setSitemapSetting(
            'seo-sitemap-offers-franchise-type-listing',
            true
        );

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        $motability = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);

        VehicleOffer::factory()->create([
            'slug' => 'ford-motability-offer',
            'franchise_id' => $ford->id,
            'type_id' => $motability->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // Top-level listing
        $this->assertStringContains(
            'http://foo.localhost/offers',
            $xml
        );
        // Top-level type listing
        $this->assertStringContains('/offers/motability', $xml);
        // Franchise listing
        $this->assertStringContains('/ford/offers', $xml);
        // Franchise type listing
        $this->assertStringContains('/ford/motability', $xml);
        // Individual offer
        $this->assertStringContains(
            '/offers/ford-motability-offer',
            $xml
        );
    }

    public function testNoListingsWhenAllDisabled(): void
    {
        $this->setSitemapSetting(
            'seo-sitemap-offers-top-level-listing',
            false
        );
        $this->setSitemapSetting(
            'seo-sitemap-offers-top-level-type-listing',
            false
        );

        $offerType = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);
        VehicleOffer::factory()->create([
            'slug' => 'some-offer',
            'type_id' => $offerType->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // Individual offer should still be present
        $this->assertStringContains('/offers/some-offer', $xml);
        // But listing pages should not
        $this->assertStringNotContains(
            'http://foo.localhost/offers</loc>',
            $xml
        );
        $this->assertStringNotContains('/offers/motability', $xml);
    }

    public function testSitemapNotGeneratedWhenFeatureDisabled(): void
    {
        // Toggle off sitemap feature
        Feature::setEnabled(tenant(), ['sitemap']);

        $this->artisan('make:sitemap')->assertExitCode(0);

        Storage::disk('file-storage')
            ->assertMissing($this->sitemapPath);
    }

    public function testTypePlaceholderUsesTypeSlugOrLiteralWhenUntyped(): void
    {
        // With a {{TYPE}} template, typed offers use their type slug as the segment;
        // offers without a type render the literal "type" placeholder value (so a {{TYPE}}
        // template should only be used where every offer has a type assigned).
        Settings::update('offers-url-path', '/{{FRANCHISE}}/{{TYPE}}/{{SLUG}}');

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        $motability = OfferType::factory()->create([
            'name' => 'Motability',
            'slug' => 'motability',
        ]);

        VehicleOffer::factory()->create([
            'slug' => 'untyped-offer',
            'franchise_id' => $ford->id,
            'published' => true,
        ]);
        VehicleOffer::factory()->create([
            'slug' => 'typed-offer',
            'franchise_id' => $ford->id,
            'type_id' => $motability->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        // Typed offer: {{TYPE}} resolves to the offer's type slug.
        $this->assertStringContains('/ford/motability/typed-offer', $xml);
        // Untyped offer: {{TYPE}} resolves to the literal placeholder fallback 'type'.
        $this->assertStringContains('/ford/type/untyped-offer', $xml);
    }

    public function testFranchiseListingHandlesZeroBasePath(): void
    {
        // Issue #3: a falsy/numeric listing-path setting must not produce /ford/0.
        $this->setSitemapSetting('seo-sitemap-offers-listing-path', '0');
        $this->setSitemapSetting('seo-sitemap-offers-franchise-listing', true);

        $ford = Franchise::factory()->create(['slug' => 'ford']);
        VehicleOffer::factory()->create([
            'slug' => 'ford-offer-1',
            'franchise_id' => $ford->id,
            'published' => true,
        ]);

        $this->artisan('make:sitemap')->assertExitCode(0);

        $xml = $this->getSitemapContent();

        $this->assertStringNotContains('/ford/0', $xml);
        // Falls back to the default /offers base path.
        $this->assertStringContains('/ford/offers', $xml);
    }

    /**
     * Helper to set a sitemap-related setting.
     */
    private function setSitemapSetting(string $key, mixed $value): void
    {
        if (Settings::exists($key)) {
            Settings::update($key, $value);
            return;
        }

        Settings::make([
            'tab' => 'Site',
            'section' => 'SEO',
            'group' => 'Sitemap',
            'name' => $key,
            'config_key' => $key,
            'type' => is_bool($value) ? 'boolean' : 'string',
            'value' => $value,
        ]);
    }

    private function getSitemapContent(): string
    {
        Storage::disk('file-storage')->assertExists($this->sitemapPath);

        return Storage::disk('file-storage')->get($this->sitemapPath);
    }

    private function assertStringContains(
        string $needle,
        string $haystack
    ): void {
        $this->assertStringContainsString($needle, $haystack);
    }

    private function assertStringNotContains(
        string $needle,
        string $haystack
    ): void {
        $this->assertStringNotContainsString($needle, $haystack);
    }
}
