<?php

namespace Tests\Feature\PublicSite;

use App\Domain\Organization\Models\Company;
use App\Domain\Recruitment\Enums\JobDemandStatus;
use App\Domain\Recruitment\Models\Employer;
use App\Domain\Recruitment\Models\JobDemand;
use App\Domain\Website\Models\DestinationCountry;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class SitemapTest extends TestCase
{
    use RefreshDatabase;

    private Company $company;

    protected function setUp(): void
    {
        parent::setUp();
        Cache::flush();
        config(['app.frontend_url' => 'https://recruitment.example']);

        $this->company = Company::factory()->create();
    }

    private function publishedJob(array $attributes = []): JobDemand
    {
        return JobDemand::factory()->for($this->company)->create(array_merge([
            'employer_id' => Employer::factory()->for($this->company)->active()->create()->id,
            'status' => JobDemandStatus::Open->value,
            'is_public' => true,
            'public_slug' => 'welder-qa',
            'publish_at' => now()->subDay(),
        ], $attributes));
    }

    public function test_the_sitemap_is_valid_xml_with_the_static_pages(): void
    {
        $response = $this->get('/sitemap.xml')
            ->assertOk()
            ->assertHeader('Content-Type', 'application/xml; charset=UTF-8');

        $xml = simplexml_load_string($response->getContent());

        $this->assertNotFalse($xml, 'The sitemap is not parseable XML.');

        // The sitemap declares a default namespace, so <loc> must be reached
        // through it rather than by property access.
        $xml->registerXPathNamespace('s', 'http://www.sitemaps.org/schemas/sitemap/0.9');
        $locations = array_map('strval', $xml->xpath('//s:loc'));

        $this->assertContains('https://recruitment.example/', $locations);
        $this->assertContains('https://recruitment.example/jobs', $locations);
        $this->assertContains('https://recruitment.example/register', $locations);
        $this->assertContains('https://recruitment.example/requisition', $locations);
    }

    public function test_published_jobs_appear_and_unpublished_ones_do_not(): void
    {
        $this->publishedJob();
        $this->publishedJob(['public_slug' => 'hidden-job', 'is_public' => false]);
        $this->publishedJob(['public_slug' => 'paused-job', 'status' => JobDemandStatus::Paused->value]);

        $content = $this->get('/sitemap.xml')->assertOk()->getContent();

        $this->assertStringContainsString('https://recruitment.example/jobs/welder-qa', $content);
        $this->assertStringNotContainsString('hidden-job', $content);
        $this->assertStringNotContainsString('paused-job', $content);
    }

    public function test_private_lookup_pages_are_never_listed(): void
    {
        $content = $this->get('/sitemap.xml')->assertOk()->getContent();

        // These are private lookups; a crawler has no business indexing them.
        $this->assertStringNotContainsString('/track', $content);
        $this->assertStringNotContainsString('/portal', $content);
        $this->assertStringNotContainsString('/app', $content);
    }

    public function test_query_string_urls_are_xml_escaped(): void
    {
        DestinationCountry::factory()->for($this->company)->create(['code' => 'QA', 'name' => 'Qatar']);

        $content = $this->get('/sitemap.xml')->assertOk()->getContent();

        // A raw ampersand would make the document invalid; there are none here,
        // but the escaping must survive if a filter is ever added.
        $this->assertNotFalse(simplexml_load_string($content));
        $this->assertStringContainsString('country=QA', $content);
    }

    public function test_robots_blocks_the_private_areas_and_points_at_the_sitemap(): void
    {
        $content = $this->get('/robots.txt')
            ->assertOk()
            ->assertHeader('Content-Type', 'text/plain; charset=UTF-8')
            ->getContent();

        foreach (['/app', '/portal', '/login', '/track', '/api/'] as $path) {
            $this->assertStringContainsString("Disallow: {$path}", $content);
        }

        $this->assertStringContainsString('Sitemap: https://recruitment.example/sitemap.xml', $content);
    }

    public function test_crawler_files_need_no_authentication(): void
    {
        $this->get('/sitemap.xml')->assertOk();
        $this->get('/robots.txt')->assertOk();
    }
}
