<?php

namespace App\Http\Controllers\Api\V1\PublicSite;

use App\Domain\Recruitment\Models\JobDemand;
use App\Domain\Website\Models\DestinationCountry;
use App\Domain\Website\Models\JobCategory;
use App\Domain\Website\Models\SuccessStory;
use App\Domain\Website\Services\SiteContentService;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

/**
 * sitemap.xml and robots.txt (§151).
 *
 * These are served by Laravel rather than the SPA because a crawler must be
 * able to read them without executing JavaScript.
 */
class SitemapController extends Controller
{
    public function __construct(private readonly SiteContentService $site) {}

    public function sitemap(Request $request): Response
    {
        $base = rtrim(config('app.frontend_url') ?: config('app.url'), '/');

        $urls = [
            ['loc' => "{$base}/", 'priority' => '1.0', 'changefreq' => 'daily'],
            ['loc' => "{$base}/jobs", 'priority' => '0.9', 'changefreq' => 'daily'],
            ['loc' => "{$base}/about", 'priority' => '0.6', 'changefreq' => 'monthly'],
            ['loc' => "{$base}/countries", 'priority' => '0.7', 'changefreq' => 'weekly'],
            ['loc' => "{$base}/stories", 'priority' => '0.5', 'changefreq' => 'weekly'],
            ['loc' => "{$base}/contact", 'priority' => '0.5', 'changefreq' => 'monthly'],
            ['loc' => "{$base}/register", 'priority' => '0.8', 'changefreq' => 'monthly'],
            ['loc' => "{$base}/requisition", 'priority' => '0.8', 'changefreq' => 'monthly'],
            // /track and /requisition/{token} are excluded: they are private
            // lookups, not pages a search engine should index.
        ];

        foreach (JobDemand::publiclyVisible()->get(['public_slug', 'updated_at']) as $job) {
            $urls[] = [
                'loc' => "{$base}/jobs/{$job->public_slug}",
                'lastmod' => $job->updated_at?->toAtomString(),
                'priority' => '0.8',
                'changefreq' => 'weekly',
            ];
        }

        foreach (DestinationCountry::published()->get(['code', 'slug', 'updated_at']) as $country) {
            $urls[] = [
                'loc' => "{$base}/jobs?country={$country->code}",
                'lastmod' => $country->updated_at?->toAtomString(),
                'priority' => '0.6',
            ];
        }

        foreach (JobCategory::published()->get(['slug', 'updated_at']) as $category) {
            $urls[] = [
                'loc' => "{$base}/jobs?category={$category->slug}",
                'lastmod' => $category->updated_at?->toAtomString(),
                'priority' => '0.6',
            ];
        }

        foreach (SuccessStory::published()->get(['slug', 'updated_at']) as $story) {
            $urls[] = [
                'loc' => "{$base}/stories#{$story->slug}",
                'lastmod' => $story->updated_at?->toAtomString(),
                'priority' => '0.4',
            ];
        }

        return response($this->render($urls), 200, [
            'Content-Type' => 'application/xml; charset=UTF-8',
        ]);
    }

    public function robots(): Response
    {
        $base = rtrim(config('app.frontend_url') ?: config('app.url'), '/');

        $lines = [
            'User-agent: *',
            // Everything behind a login, plus the private lookup pages.
            'Disallow: /app',
            'Disallow: /portal',
            'Disallow: /login',
            'Disallow: /track',
            'Disallow: /requisition/',
            'Disallow: /api/',
            '',
            "Sitemap: {$base}/sitemap.xml",
        ];

        return response(implode("\n", $lines)."\n", 200, [
            'Content-Type' => 'text/plain; charset=UTF-8',
        ]);
    }

    /** @param array<int, array<string, string|null>> $urls */
    private function render(array $urls): string
    {
        $xml = new \SimpleXMLElement(
            '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"/>'
        );

        foreach ($urls as $url) {
            $node = $xml->addChild('url');
            // addChild does not escape, so ampersands in query strings must be
            // encoded before they reach it.
            $node->addChild('loc', htmlspecialchars($url['loc'], ENT_XML1));

            foreach (['lastmod', 'changefreq', 'priority'] as $key) {
                if (! empty($url[$key])) {
                    $node->addChild($key, $url[$key]);
                }
            }
        }

        return $xml->asXML();
    }
}
