<?php

namespace App\Http\Controllers;

use Illuminate\Http\Response;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use App\Models\PublishingPage;
use App\Models\HealthInstitution;
use App\Models\DigitalMachine;
use App\Models\TrainingPackage;
use App\Models\Mentorship\Programme as MentorshipProgramme;
use App\Models\Mentorship\Category as MentorshipCategory;
use App\Models\RPublication;
use App\Models\KnowledgeBaseQuestion;
use App\Models\KnowledgeBaseCategory;
use App\Models\Discussion;
use App\Models\DiscussionTopic;
use App\Models\RCampaign;
use App\Models\RHealthStory;
use App\Models\RPartner;
use App\Models\MembershipCategory;
use App\Models\LoanProduct;
use App\Models\SelfPaced\Course as SelfPacedCourse;

/**
 * SitemapController
 *
 * Generates a comprehensive XML sitemap covering all public pages:
 *   - Static pages (site.blade.php layout)
 *   - Self-paced learning portal pages (self-paced-landing.blade.php layout)
 *   - Auth / guest pages (guest.blade.php layout)
 *   - All dynamic content (institutions, machines, training, mentorship,
 *     research, knowledge base, discussions, advocacy, partners, etc.)
 *
 * Accessible at: GET /sitemap.xml  (no authentication required)
 * Google Search Console: add APP_URL/sitemap.xml
 *
 * Caches the URL list for 60 minutes to avoid hammering the database on
 * every Googlebot crawl. Clear with: php artisan cache:clear
 */
class SitemapController extends Controller
{
    /** Cache TTL in seconds (60 minutes). */
    private const CACHE_TTL = 3600;

    /** Cache key for the sitemap URL list. */
    private const CACHE_KEY = 'sitemap.urls';

    // =========================================================================
    // Public entry points
    // =========================================================================

    /**
     * Generate and return the XML sitemap.
     *
     * Accessible at: GET /sitemap.xml
     * Submit to Google Search Console as: APP_URL/sitemap.xml
     *
     * Add ?refresh=1 to force-regenerate (bypasses cache).
     * Only works when APP_DEBUG=true or from a trusted IP to prevent abuse.
     */
    public function index(\Illuminate\Http\Request $request): Response
    {
        // Allow cache bypass in debug mode or when explicitly requested
        if ($request->boolean('refresh') && config('app.debug')) {
            Cache::forget(self::CACHE_KEY);
        }

        $urls = Cache::remember(self::CACHE_KEY, self::CACHE_TTL, function () {
            return $this->collectPublicUrls();
        });

        return response()
            ->view('sitemap.index', compact('urls'))
            ->header('Content-Type', 'application/xml; charset=UTF-8')
            ->header('X-Robots-Tag', 'noindex');
    }

    /**
     * Serve a dynamic robots.txt that always references the correct APP_URL.
     *
     * Accessible at: GET /robots.txt
     */
    public function robots(): Response
    {
        $content = view('sitemap.robots')->render();

        return response($content, 200)
            ->header('Content-Type', 'text/plain; charset=UTF-8');
    }

    // =========================================================================
    // URL collection
    // =========================================================================

    /**
     * Collect every public URL that should appear in the sitemap.
     *
     * Sections:
     *  1. Static pages (site layout)
     *  2. Self-paced landing pages
     *  3. Guest / auth pages
     *  4. Dynamic: publishing pages
     *  5. Dynamic: membership categories
     *  6. Dynamic: loan products
     *  7. Dynamic: health institutions
     *  8. Dynamic: digital machines
     *  9. Dynamic: training packages
     * 10. Dynamic: mentorship programmes + categories
     * 11. Dynamic: research publications
     * 12. Dynamic: knowledge base categories + questions
     * 13. Dynamic: discussion topics + discussions
     * 14. Dynamic: advocacy campaigns + health stories
     * 15. Dynamic: partners
     * 16. Dynamic: self-paced courses
     */
    private function collectPublicUrls(): array
    {
        $urls = [];

        $this->addStaticPages($urls);
        $this->addSelfPacedStaticPages($urls);
        $this->addGuestPages($urls);
        $this->addPublishingPages($urls);
        $this->addMembershipCategories($urls);
        $this->addLoanProducts($urls);
        $this->addHealthInstitutions($urls);
        $this->addDigitalMachines($urls);
        $this->addTrainingPackages($urls);
        $this->addMentorshipContent($urls);
        $this->addResearchPublications($urls);
        $this->addKnowledgeBaseContent($urls);
        $this->addDiscussionContent($urls);
        $this->addAdvocacyContent($urls);
        $this->addPartners($urls);
        $this->addSelfPacedCourses($urls);

        return $urls;
    }

    // =========================================================================
    // Helpers
    // =========================================================================

    /**
     * Safely generate a named route URL.
     * Returns null if the route does not exist or generation fails.
     */
    private function safeRoute(string $name, mixed $parameters = []): ?string
    {
        try {
            return route($name, $parameters);
        } catch (\Throwable $e) {
            Log::warning("Sitemap: route '{$name}' could not be generated — " . $e->getMessage());
            return null;
        }
    }

    /**
     * Add a URL entry only if the loc is non-null.
     */
    private function addUrl(array &$urls, ?string $loc, string $changefreq, string $priority, string $lastmod): void
    {
        if ($loc === null) {
            return;
        }

        $urls[] = [
            'loc'        => htmlspecialchars($loc, ENT_XML1 | ENT_QUOTES, 'UTF-8'),
            'changefreq' => $changefreq,
            'priority'   => $priority,
            'lastmod'    => $lastmod,
        ];
    }

    // =========================================================================
    // Section helpers
    // =========================================================================

    /**
     * 1. Static pages that use the site.blade.php layout.
     */
    private function addStaticPages(array &$urls): void
    {
        $today = now()->toDateString();

        $pages = [
            // Homepage
            ['loc' => url('/'),                                                    'changefreq' => 'daily',   'priority' => '1.0'],

            // Membership
            ['loc' => $this->safeRoute('membership.categories.index'),            'changefreq' => 'weekly',  'priority' => '0.9'],
            ['loc' => $this->safeRoute('membership.apply'),                       'changefreq' => 'monthly', 'priority' => '0.8'],
            ['loc' => $this->safeRoute('membership.check-status'),                'changefreq' => 'monthly', 'priority' => '0.6'],

            // Loans (public)
            ['loc' => $this->safeRoute('loans.index'),                            'changefreq' => 'weekly',  'priority' => '0.8'],

            // Training
            ['loc' => $this->safeRoute('public.training.index'),                  'changefreq' => 'weekly',  'priority' => '0.9'],
            ['loc' => $this->safeRoute('public.training.certificates.verify-page'), 'changefreq' => 'monthly', 'priority' => '0.5'],

            // Mentorship
            ['loc' => $this->safeRoute('mentorship.public.index'),                'changefreq' => 'weekly',  'priority' => '0.9'],
            ['loc' => $this->safeRoute('mentorship.public.apply'),                'changefreq' => 'monthly', 'priority' => '0.7'],
            ['loc' => $this->safeRoute('mentorship.public.check-status'),         'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('mentorship.certificates.verify-page'),    'changefreq' => 'monthly', 'priority' => '0.5'],

            // Research
            ['loc' => $this->safeRoute('research.public.index'),                  'changefreq' => 'weekly',  'priority' => '0.8'],
            ['loc' => $this->safeRoute('research.public.featured'),               'changefreq' => 'weekly',  'priority' => '0.7'],

            // Knowledge Base
            ['loc' => $this->safeRoute('public.knowledge-base.index'),            'changefreq' => 'weekly',  'priority' => '0.8'],

            // Discussions
            ['loc' => $this->safeRoute('public.discussions.index'),               'changefreq' => 'daily',   'priority' => '0.7'],

            // Advocacy
            ['loc' => $this->safeRoute('advocacy.public.index'),                  'changefreq' => 'weekly',  'priority' => '0.8'],
            ['loc' => $this->safeRoute('advocacy.public.stories.index'),          'changefreq' => 'daily',   'priority' => '0.7'],
            ['loc' => $this->safeRoute('advocacy.public.stories.create'),         'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('advocacy.public.polls.active'),           'changefreq' => 'weekly',  'priority' => '0.6'],

            // Partners
            ['loc' => $this->safeRoute('partners.public.index'),                  'changefreq' => 'weekly',  'priority' => '0.7'],
            ['loc' => $this->safeRoute('partners.public.become-partner'),         'changefreq' => 'monthly', 'priority' => '0.6'],

            // Health Directory
            ['loc' => $this->safeRoute('institutions.index'),                     'changefreq' => 'weekly',  'priority' => '0.8'],
            ['loc' => $this->safeRoute('institutions.register'),                  'changefreq' => 'monthly', 'priority' => '0.6'],
            ['loc' => $this->safeRoute('machines.index'),                         'changefreq' => 'weekly',  'priority' => '0.7'],

            // Contributor
            ['loc' => $this->safeRoute('public.contributor.become'),              'changefreq' => 'monthly', 'priority' => '0.6'],
            ['loc' => $this->safeRoute('public.contributor.register'),            'changefreq' => 'monthly', 'priority' => '0.5'],

            // Static CMS pages (always included even if not yet in DB)
            ['loc' => $this->safeRoute('pages.show', 'about-sshia'),              'changefreq' => 'monthly', 'priority' => '0.8'],
            ['loc' => $this->safeRoute('pages.show', 'vision'),                   'changefreq' => 'monthly', 'priority' => '0.7'],
            ['loc' => $this->safeRoute('pages.show', 'mission'),                  'changefreq' => 'monthly', 'priority' => '0.7'],
            ['loc' => $this->safeRoute('pages.show', 'what-we-do'),               'changefreq' => 'monthly', 'priority' => '0.7'],
            ['loc' => $this->safeRoute('pages.show', 'governance'),               'changefreq' => 'monthly', 'priority' => '0.7'],

            // Legal / footer pages
            ['loc' => $this->safeRoute('pages.privacy'),                          'changefreq' => 'yearly',  'priority' => '0.3'],
            ['loc' => $this->safeRoute('pages.terms'),                            'changefreq' => 'yearly',  'priority' => '0.3'],
            ['loc' => $this->safeRoute('pages.support'),                          'changefreq' => 'monthly', 'priority' => '0.5'],
        ];

        foreach ($pages as $page) {
            $this->addUrl($urls, $page['loc'], $page['changefreq'], $page['priority'], $today);
        }
    }

    /**
     * 2. Self-paced learning portal static pages (self-paced-landing.blade.php).
     */
    private function addSelfPacedStaticPages(array &$urls): void
    {
        $today = now()->toDateString();

        $this->addUrl($urls, $this->safeRoute('self-paced.public.index'),  'weekly',  '0.9', $today);
        $this->addUrl($urls, $this->safeRoute('self-paced.public.verify'), 'monthly', '0.5', $today);
    }

    /**
     * 3. Guest / auth pages (guest.blade.php layout).
     *    Login and register are included for discoverability; role-specific
     *    registration forms are also included.
     */
    private function addGuestPages(array &$urls): void
    {
        $today = now()->toDateString();

        $pages = [
            ['loc' => $this->safeRoute('login'),                              'changefreq' => 'yearly',  'priority' => '0.4'],
            ['loc' => $this->safeRoute('register'),                           'changefreq' => 'yearly',  'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.member'),               'changefreq' => 'monthly', 'priority' => '0.6'],
            ['loc' => $this->safeRoute('register.role.trainer'),              'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.trainee'),              'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.mentor'),               'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.mentee'),               'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.partner'),              'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.contributor'),          'changefreq' => 'monthly', 'priority' => '0.5'],
            ['loc' => $this->safeRoute('register.role.health-marketor'),      'changefreq' => 'monthly', 'priority' => '0.5'],
        ];

        foreach ($pages as $page) {
            $this->addUrl($urls, $page['loc'], $page['changefreq'], $page['priority'], $today);
        }
    }

    /**
     * 4. Dynamic publishing pages (CMS pages created via admin dashboard).
     */
    private function addPublishingPages(array &$urls): void
    {
        try {
            PublishingPage::where('is_published', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (PublishingPage $page) use (&$urls) {
                    $loc = $this->safeRoute('pages.show', $page->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.6',
                        $page->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load publishing pages — ' . $e->getMessage());
        }
    }

    /**
     * 5. Membership category detail pages.
     */
    private function addMembershipCategories(array &$urls): void
    {
        try {
            MembershipCategory::where('is_active', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('sort_order')
                ->each(function (MembershipCategory $cat) use (&$urls) {
                    $loc = $this->safeRoute('membership.categories.show', $cat->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.7',
                        $cat->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load membership categories — ' . $e->getMessage());
        }
    }

    /**
     * 6. Loan product detail pages.
     */
    private function addLoanProducts(array &$urls): void
    {
        try {
            LoanProduct::where('is_active', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('sort_order')
                ->each(function (LoanProduct $product) use (&$urls) {
                    $loc = $this->safeRoute('loans.show', $product->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.6',
                        $product->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load loan products — ' . $e->getMessage());
        }
    }

    /**
     * 7. Health institution detail pages.
     */
    private function addHealthInstitutions(array &$urls): void
    {
        try {
            HealthInstitution::where('is_published', true)
                ->where('listing_status', 'published')
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (HealthInstitution $institution) use (&$urls) {
                    $loc = $this->safeRoute('institutions.show', $institution->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.7',
                        $institution->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load health institutions — ' . $e->getMessage());
        }
    }

    /**
     * 8. Digital machine detail pages.
     */
    private function addDigitalMachines(array &$urls): void
    {
        try {
            DigitalMachine::where('is_published', true)
                ->where('listing_status', 'published')
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (DigitalMachine $machine) use (&$urls) {
                    $loc = $this->safeRoute('machines.show', $machine->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.6',
                        $machine->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load digital machines — ' . $e->getMessage());
        }
    }

    /**
     * 9. Training package detail pages.
     */
    private function addTrainingPackages(array &$urls): void
    {
        try {
            TrainingPackage::where('status', 'active')
                ->where('is_public', true)
                ->select(['id', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (TrainingPackage $package) use (&$urls) {
                    $loc = $this->safeRoute('public.training.show', $package->id);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.7',
                        $package->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load training packages — ' . $e->getMessage());
        }
    }

    /**
     * 10. Mentorship programmes and category pages.
     */
    private function addMentorshipContent(array &$urls): void
    {
        // Mentorship category pages
        try {
            MentorshipCategory::where('is_active', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('sort_order')
                ->each(function (MentorshipCategory $cat) use (&$urls) {
                    $loc = $this->safeRoute('mentorship.public.category', $cat->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.6',
                        $cat->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load mentorship categories — ' . $e->getMessage());
        }

        // Mentorship programme detail pages
        try {
            MentorshipProgramme::where('is_active', true)
                ->select(['id', 'slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (MentorshipProgramme $programme) use (&$urls) {
                    $identifier = $programme->slug ?: $programme->id;
                    $loc = $this->safeRoute('mentorship.public.programme', $identifier);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.7',
                        $programme->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load mentorship programmes — ' . $e->getMessage());
        }
    }

    /**
     * 11. Research publication detail pages.
     */
    private function addResearchPublications(array &$urls): void
    {
        try {
            RPublication::whereIn('status', [RPublication::STATUS_APPROVED, RPublication::STATUS_FEATURED])
                ->select(['id', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (RPublication $publication) use (&$urls) {
                    $loc = $this->safeRoute('research.public.show', $publication->id);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.7',
                        $publication->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load research publications — ' . $e->getMessage());
        }
    }

    /**
     * 12. Knowledge base category and question pages.
     */
    private function addKnowledgeBaseContent(array &$urls): void
    {
        // KB category pages
        try {
            KnowledgeBaseCategory::where('is_active', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('sort_order')
                ->each(function (KnowledgeBaseCategory $cat) use (&$urls) {
                    $loc = $this->safeRoute('public.knowledge-base.category', $cat->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.6',
                        $cat->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load KB categories — ' . $e->getMessage());
        }

        // KB question pages
        try {
            KnowledgeBaseQuestion::where('is_published', true)
                ->where('status', KnowledgeBaseQuestion::STATUS_PUBLISHED)
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (KnowledgeBaseQuestion $question) use (&$urls) {
                    $loc = $this->safeRoute('public.knowledge-base.show', $question->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.6',
                        $question->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load KB questions — ' . $e->getMessage());
        }
    }

    /**
     * 13. Discussion topic and individual discussion pages.
     */
    private function addDiscussionContent(array &$urls): void
    {
        // Discussion topic pages
        try {
            DiscussionTopic::where('is_active', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('sort_order')
                ->each(function (DiscussionTopic $topic) use (&$urls) {
                    $loc = $this->safeRoute('public.discussions.topic', $topic->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.6',
                        $topic->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load discussion topics — ' . $e->getMessage());
        }

        // Individual discussion pages
        try {
            Discussion::where('status', Discussion::STATUS_PUBLISHED)
                ->where('is_active', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (Discussion $discussion) use (&$urls) {
                    $loc = $this->safeRoute('public.discussions.show', $discussion->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'daily',
                        '0.6',
                        $discussion->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load discussions — ' . $e->getMessage());
        }
    }

    /**
     * 14. Advocacy campaigns and health story pages.
     */
    private function addAdvocacyContent(array &$urls): void
    {
        // Advocacy campaign pages
        try {
            RCampaign::where('status', RCampaign::STATUS_ACTIVE)
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (RCampaign $campaign) use (&$urls) {
                    $loc = $this->safeRoute('advocacy.public.campaigns.show', $campaign->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.7',
                        $campaign->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load advocacy campaigns — ' . $e->getMessage());
        }

        // Health story pages
        try {
            RHealthStory::where('status', RHealthStory::STATUS_PUBLISHED)
                ->whereNotNull('published_at')
                ->where('published_at', '<=', now())
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (RHealthStory $story) use (&$urls) {
                    $loc = $this->safeRoute('advocacy.public.stories.show', $story->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.6',
                        $story->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load health stories — ' . $e->getMessage());
        }
    }

    /**
     * 15. Partner directory pages.
     */
    private function addPartners(array &$urls): void
    {
        try {
            RPartner::where('partnership_status', RPartner::PARTNERSHIP_ACTIVE)
                ->where('show_in_directory', true)
                ->select(['slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (RPartner $partner) use (&$urls) {
                    $loc = $this->safeRoute('partners.public.show', $partner->slug);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'monthly',
                        '0.6',
                        $partner->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load partners — ' . $e->getMessage());
        }
    }

    /**
     * 16. Self-paced course detail pages (self-paced-landing.blade.php layout).
     *
     * The Course model uses the default route key (id), so we pass the id
     * to the route helper. The slug is stored but not used for route binding.
     */
    private function addSelfPacedCourses(array &$urls): void
    {
        try {
            SelfPacedCourse::where('status', 'published')
                ->select(['id', 'slug', 'updated_at'])
                ->orderBy('updated_at', 'desc')
                ->each(function (SelfPacedCourse $course) use (&$urls) {
                    $loc = $this->safeRoute('self-paced.public.courses.show', $course->id);
                    $this->addUrl(
                        $urls,
                        $loc,
                        'weekly',
                        '0.8',
                        $course->updated_at?->toDateString() ?? now()->toDateString()
                    );
                });
        } catch (\Throwable $e) {
            Log::warning('Sitemap: failed to load self-paced courses — ' . $e->getMessage());
        }
    }
}
