<?php

namespace App\Console\Commands;

use App\Facades\Feature;
use App\Facades\Settings;
use App\Facades\Site;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;
use Mtc\MercuryDataModels\CarConfiguratorModel;
use Mtc\MercuryDataModels\Contracts\ModelWithUrlPath;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Franchise;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\OfferType;
use Mtc\MercuryDataModels\Page;
use Mtc\MercuryDataModels\Property;
use Mtc\MercuryDataModels\PropertyCategory;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;

class MakeSitemap extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:sitemap';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate sitemap.xml for a site';

    private Sitemap $sitemap;

    /**
     * Execute the console command.
     */
    public function handle()
    {
        if (!Feature::isEnabled('sitemap')) {
            return self::SUCCESS;
        }

        $this->sitemap = Sitemap::create();
        Page::query()->active()->get()
            ->each(fn(Page $page) => $this->addToSitemap($page));

        if (Feature::isEnabled('new-vehicles')) {
            NewCar::query()->active()->with('franchise')->get()
                ->each(fn(NewCar $page) => $this->addNewCarToSitemap($page));

            VehicleOffer::query()->active()->with(['franchise', 'offerType'])->get()
                ->each(fn(VehicleOffer $page) => $this->addOfferToSitemap($page));

            $this->addOfferListingPages();
        }

        if (Feature::isEnabled('dealerships')) {
            Dealership::query()->where('active', 1)->get()->each(fn(Dealership $page) => $this->addToSitemap($page));
        }

        if (Settings::get('seo-sitemap-include-srp')) {
            match (Settings::get('seo-sitemap-srp-type')) {
                'fixed-path' => $this->franchisePlusPathSrp(ltrim(Settings::get('seo-sitemap-srp-path'), '/'), false),
                'path-plus-franchise' => $this->franchisePlusPathSrp(ltrim(Settings::get('seo-sitemap-srp-path'), '/')),
                default => $this->conditionTypeSrp(),
            };
        }

        if (Settings::get('seo-sitemap-include-fpa')) {
            Vehicle::query()->active()->get()
                ->each(fn(Vehicle $page) => $this->addToSitemap($page));
        }

        if (Feature::isEnabled('car-configurator')) {
            CarConfiguratorModel::query()->where('active', 1)->get()
                ->each(fn(CarConfiguratorModel $page) => $this->addToSitemap($page));
        }

        collect(explode("\n", Settings::get('seo-sitemap-fixed-urls', '')))
            ->filter()
            ->each(fn($url) => $this->sitemap->add(
                Url::create(Site::url($url))
                    ->setPriority($this->calculatePriority($url))
            ));

        $this->addPropertyCategoryEntries();
        $this->addPageCategoryPagination();

        $this->sitemap->writeToDisk('file-storage', tenant('id') . '/sitemap.xml', true);
        return self::SUCCESS;
    }

    /**
     * Add property entries from categories that have sitemap enabled.
     */
    private function addPropertyCategoryEntries(): void
    {
        PropertyCategory::query()
            ->where('active', 1)
            ->get()
            ->filter(fn(PropertyCategory $category) => ($category->data['sitemap_enabled'] ?? false))
            ->each(function (PropertyCategory $category) {
                $basePath = ltrim($category->data['sitemap_base_path'] ?? $category->slug, '/');

                $propertyCount = $category->properties()
                    ->where('active', 1)
                    ->count();

                $this->addPaginatedUrls('/' . $basePath, $propertyCount, 15);

                $category->properties()
                    ->where('active', 1)
                    ->get()
                    ->each(function (Property $property) use ($basePath) {
                        $path = '/' . $basePath . '/' . $property->slug;
                        $this->sitemap->add(
                            Url::create(Site::url($path))
                                ->setLastModificationDate($property->updated_at)
                                ->setPriority($this->calculatePriority($path))
                        );
                    });
            });
    }

    /**
     * Add pagination URLs for page category listings (e.g. /news, /offers).
     */
    private function addPageCategoryPagination(): void
    {
        Page::query()->active()
            ->whereNotNull('category')
            ->where('category', '!=', '')
            ->selectRaw('category, COUNT(*) as total')
            ->groupBy('category')
            ->get()
            ->each(function ($row) {
                $this->addPaginatedUrls('/' . $row->category, $row->total, 15);
            });
    }

    /**
     * Add offer listing pages to the sitemap based on site configuration.
     */
    private function addOfferListingPages(): void
    {
        $basePath = $this->getOfferListingBasePath();
        $perPage = (int) (Settings::get('filter-offers-per-page') ?? 15);

        if (Settings::get('seo-sitemap-offers-top-level-listing', true)) {
            $this->addListingUrl($basePath);
            $totalOffers = VehicleOffer::query()->active()->count();
            $this->addPaginatedUrls($basePath, $totalOffers, $perPage);
        }

        if (Settings::get('seo-sitemap-offers-top-level-type-listing', true)) {
            $this->addOfferTypeListingPages($basePath, $perPage);
        }

        if (Settings::get('seo-sitemap-offers-franchise-listing', false)) {
            $this->addFranchiseOfferListingPages($basePath, $perPage);
        }

        if (Settings::get('seo-sitemap-offers-franchise-type-listing', false)) {
            $this->addFranchiseTypeListingPages($perPage);
        }
    }

    private function getOfferListingBasePath(): string
    {
        $listingPath = Settings::get('seo-sitemap-offers-listing-path', '/offers');
        return '/' . trim($listingPath, '/');
    }

    private function addListingUrl(string $path): void
    {
        $this->sitemap->add(
            Url::create(Site::url($path))
                ->setChangeFrequency('daily')
                ->setPriority($this->calculatePriority($path))
        );
    }

    private function addOfferTypeListingPages(
        string $basePath,
        int $perPage
    ): void {
        OfferType::query()
            ->whereHas('offers', fn($q) => $q->active())
            ->get()
            ->each(function (OfferType $type) use ($basePath, $perPage) {
                $typePath = $basePath . '/' . $type->slug;
                $this->addListingUrl($typePath);

                $typeCount = $type->offers()->active()->count();
                $this->addPaginatedUrls($typePath, $typeCount, $perPage);
            });
    }

    private function addFranchiseOfferListingPages(
        string $basePath,
        int $perPage
    ): void {
        Franchise::query()
            ->whereHas('offers', fn($q) => $q->active())
            ->get()
            ->each(function (Franchise $franchise) use ($basePath, $perPage) {
                $path = '/' . $franchise->slug . $basePath;
                $this->addListingUrl($path);

                $count = $franchise->offers()->active()->count();
                $this->addPaginatedUrls($path, $count, $perPage);
            });
    }

    private function addFranchiseTypeListingPages(int $perPage): void
    {
        $franchises = Franchise::query()
            ->whereHas('offers', fn($q) => $q->active())
            ->get()
            ->keyBy('id');

        $offerTypes = OfferType::query()
            ->whereHas('offers', fn($q) => $q->active())
            ->get()
            ->keyBy('id');

        VehicleOffer::query()
            ->active()
            ->whereNotNull('franchise_id')
            ->whereNotNull('type_id')
            ->selectRaw('franchise_id, type_id, COUNT(*) as total')
            ->groupBy('franchise_id', 'type_id')
            ->get()
            ->each(function ($row) use ($franchises, $offerTypes, $perPage) {
                $franchise = $franchises->get($row->franchise_id);
                $type = $offerTypes->get($row->type_id);

                if (!$franchise || !$type) {
                    return;
                }

                $path = '/' . $franchise->slug . '/' . $type->slug;
                $this->addListingUrl($path);
                $this->addPaginatedUrls($path, $row->total, $perPage);
            });
    }

    private function addToSitemap(ModelWithUrlPath $page): void
    {
        $path = $page->urlPath();
        $this->sitemap->add(
            Url::create(Site::url($path))
                ->setLastModificationDate($page->updated_at)
                ->setPriority($this->calculatePriority($path))
        );
    }

    private function addNewCarToSitemap(NewCar $newCar): void
    {
        $path = $newCar->urlPath();

        if (
            $newCar->franchise
            && !str_starts_with(ltrim($path, '/'), $newCar->franchise->slug . '/')
            && $this->shouldPrefixWithFranchise($newCar->franchise)
        ) {
            $path = '/' . $newCar->franchise->slug . '/' . ltrim($path, '/');
        }

        $this->sitemap->add(
            Url::create(Site::url($path))
                ->setLastModificationDate($newCar->updated_at)
                ->setPriority($this->calculatePriority($path))
        );
    }

    private function addOfferToSitemap(VehicleOffer $offer): void
    {
        $path = $offer->urlPath();

        if (
            $offer->franchise
            && !str_starts_with(ltrim($path, '/'), $offer->franchise->slug . '/')
            && $this->shouldPrefixWithFranchise($offer->franchise)
        ) {
            $path = '/' . $offer->franchise->slug . '/' . ltrim($path, '/');
        }

        $this->sitemap->add(
            Url::create(Site::url($path))
                ->setLastModificationDate($offer->updated_at)
                ->setPriority($this->calculatePriority($path))
        );
    }

    private function shouldPrefixWithFranchise(Franchise $franchise): bool
    {
        $srpType = Settings::get('seo-sitemap-srp-type');

        if ($srpType === 'path-plus-franchise') {
            return true;
        }

        return $franchise->data['sitemap-new-cars-enabled'] ?? true;
    }

    /**
     * Calculate priority based on URL depth.
     * Homepage and first level pages get 1.0, each additional level reduces by 0.1
     * Minimum priority is 0.1
     */
    private function calculatePriority(string $path): float
    {
        $path = trim($path, '/');

        // Homepage
        if (empty($path) || $path === 'home') {
            return 1.0;
        }

        $depth = substr_count($path, '/') + 1;
        $priority = 1.0 - (($depth - 1) * 0.1);

        return max(0.1, $priority);
    }

    /**
     * Add paginated URLs to the sitemap for a listing page.
     * Starts from page 2 since the base URL represents page 1.
     */
    private function addPaginatedUrls(string $basePath, int $totalItems, int $perPage): void
    {
        if (!Settings::get('seo-sitemap-include-pagination', true)) {
            return;
        }

        $totalPages = (int) ceil($totalItems / $perPage);

        for ($page = 2; $page <= $totalPages; $page++) {
            $this->sitemap->add(
                Url::create(Site::url($basePath) . '?page=' . $page)
                    ->setChangeFrequency('daily')
                    ->setPriority(max(0.1, $this->calculatePriority($basePath) - 0.1))
            );
        }
    }

    /**
     * SRP is a single fixed path - no differentiation between condition (new/used) or type (car/lcv),
     * no franchise specific urls
     */
    private function fixedPathSrp(
        string $srpPath,
        array $onlyDealershipIds = []
    ): void {
        $perPage = (int) (Settings::get('filter-results-per-page') ?? 15);

        if (empty($onlyDealershipIds)) {
            $this->sitemap->add(
                Url::create(Site::url($srpPath))
                    ->setChangeFrequency('daily')
                    ->setPriority($this->calculatePriority($srpPath))
            );

            $baseCount = Vehicle::query()->active()
                ->count();
            $this->addPaginatedUrls($srpPath, $baseCount, $perPage);
        }

        if (Settings::get('seo-sitemap-include-srp-makes')) {
            VehicleMake::query()
                ->whereHas('vehicles', fn($query) => $query->active()
                    ->when(!empty($onlyDealershipIds), fn($q) => $q->whereIn('dealership_id', $onlyDealershipIds)))
                ->get()
                ->each(function (VehicleMake $make) use ($srpPath, $perPage, $onlyDealershipIds) {
                    $makePath = '/' . $srpPath . '/' . $make->slug;
                    $this->sitemap->add(
                        Url::create(Site::url($makePath))
                            ->setChangeFrequency('daily')
                            ->setPriority($this->calculatePriority($makePath))
                    );

                    $makeCount = Vehicle::query()->active()
                        ->where('make_id', $make->id)
                        ->when(!empty($onlyDealershipIds), fn($q) => $q->whereIn('dealership_id', $onlyDealershipIds))
                        ->count();
                    $this->addPaginatedUrls($makePath, $makeCount, $perPage);

                    if (Settings::get('seo-sitemap-include-srp-models')) {
                        $this->srpModels($srpPath, $make, $perPage, $onlyDealershipIds);
                    }
                });
        }
    }

    /**
     * Get dealership IDs for a franchise, respecting multi-franchise setting.
     */
    private function getFranchiseDealershipIds(Franchise $franchise): array
    {
        if (Settings::get('dealerships-multiple-franchises')) {
            return $franchise->dealerships()->pluck('dealerships.id')->toArray();
        }

        return Dealership::query()
            ->where('franchise_id', $franchise->id)
            ->pluck('id')
            ->toArray();
    }

    private function franchisePlusPathSrp(string $srpPath, bool $defaultEnabled = true): void
    {
        $franchises = Franchise::query()->get();

        $enabledFranchises = $defaultEnabled
            ? $franchises
            : $franchises->filter(
                fn(Franchise $franchise) => ($franchise->data['sitemap-srp-enabled'] ?? false)
            );

        // Base SRP includes all vehicles (no exclusions)
        $this->fixedPathSrp($srpPath);

        // Franchise-specific SRPs filter to vehicles at franchise dealerships
        $enabledFranchises->each(function (Franchise $franchise) use ($srpPath) {
            $franchiseSrpPath = !empty($franchise->data['sitemap-srp-path'])
                ? ltrim($franchise->data['sitemap-srp-path'], '/')
                : $srpPath;

            $franchiseDealershipIds = $this->getFranchiseDealershipIds($franchise);

            if (!empty($franchiseDealershipIds)) {
                $this->fixedPathSrp(
                    $franchise->slug . '/' . $franchiseSrpPath,
                    $franchiseDealershipIds
                );
            }
        });
    }

    private function conditionTypeSrp(): void
    {
        $perPage = (int) (Settings::get('filter-results-per-page') ?? 15);

        $types = array_filter([
            'used-cars' => Vehicle::query()->active()->where('type', 'car')->used()->exists(),
            'used-vans' => Vehicle::query()->active()->where('type', 'lcv')->used()->exists(),
            'used-motorcycles' => Vehicle::query()->active()->where('type', 'motorcycle')->used()->exists(),
            'new-cars' => Vehicle::query()->active()->where('type', 'car')->new()->exists(),
            'new-vans' => Vehicle::query()->active()->where('type', 'lcv')->new()->exists(),
            'new-motorcycles' => Vehicle::query()->active()->where('type', 'motorcycle')->new()->exists(),
        ]);

        foreach ($types as $type => $has_records) {
            if ($type === 'new-cars' && Settings::get('seo-sitemap-new-cars-called-stock')) {
                $srpPath = 'new-stock';
            } else {
                $srpPath = $type;
            }
            $this->sitemap->add(
                Url::create(Site::url($srpPath))
                    ->setChangeFrequency('daily')
                    ->setPriority($this->calculatePriority($srpPath))
            );

            $baseCount = $this->setConditionQuery($type, Vehicle::query()->active())->count();
            $this->addPaginatedUrls($srpPath, $baseCount, $perPage);

            if (Settings::get('seo-sitemap-include-srp-makes')) {
                VehicleMake::query()
                    ->whereHas('vehicles', fn($query) => $this->setConditionQuery($type, $query->active()))->get()
                    ->each(function (VehicleMake $make) use ($srpPath, $type, $perPage) {
                        $makePath = '/' . $srpPath . '/' . $make->slug;
                        $this->sitemap->add(
                            Url::create(Site::url($makePath))
                                ->setChangeFrequency('daily')
                                ->setPriority($this->calculatePriority($makePath))
                        );

                        $makeCount = $this->setConditionQuery($type, Vehicle::query()->active())
                            ->where('make_id', $make->id)->count();
                        $this->addPaginatedUrls($makePath, $makeCount, $perPage);

                        if (Settings::get('seo-sitemap-include-srp-models')) {
                            VehicleModel::query()
                                ->where('make_id', $make->id)
                                ->whereHas('vehicles', fn($query) => $this->setConditionQuery($type, $query->active()))
                                ->get()
                                ->each(function (VehicleModel $model) use ($srpPath, $make, $type, $perPage) {
                                    $modelPath = '/' . $srpPath . '/' . $make->slug . '/' . $model->slug;
                                    $this->sitemap->add(
                                        Url::create(Site::url($modelPath))
                                            ->setChangeFrequency('daily')
                                            ->setPriority($this->calculatePriority($modelPath))
                                    );

                                    $modelCount = $this->setConditionQuery($type, Vehicle::query()->active())
                                        ->where('make_id', $make->id)
                                        ->where('model_id', $model->id)
                                        ->count();
                                    $this->addPaginatedUrls($modelPath, $modelCount, $perPage);
                                });
                        }
                    });
            }
        }
    }

    private function setConditionQuery(string $type, Builder $query)
    {
        return match ($type) {
            'used-cars' => $query->where('type', 'car')->used(),
            'used-vans' => $query->where('type', 'lcv')->used(),
            'used-motorcycles' => $query->where('type', 'motorcycle')->used(),
            'new-cars' => $query->where('type', 'car')->new(),
            'new-vans' => $query->where('type', 'lcv')->new(),
            'new-motorcycles' => $query->where('type', 'motorcycle')->new(),
            default => throw new \Exception('Unrecognized condition type'),
        };
    }

    private function srpModels(string $srpPath, VehicleMake $make, int $perPage, array $onlyDealershipIds)
    {
        VehicleModel::query()
            ->where('make_id', $make->id)
            ->whereHas('vehicles', fn($query) => $query->active()
                ->when(!empty($onlyDealershipIds), fn($q) => $q->whereIn('dealership_id', $onlyDealershipIds)))
            ->get()
            ->each(function (VehicleModel $model) use ($srpPath, $make, $perPage, $onlyDealershipIds) {
                $modelPath = '/' . $srpPath . '/' . $make->slug . '/' . $model->slug;
                $this->sitemap->add(
                    Url::create(Site::url($modelPath))
                        ->setChangeFrequency('daily')
                        ->setPriority($this->calculatePriority($modelPath))
                );

                $modelCount = Vehicle::query()->active()
                    ->where('make_id', $make->id)
                    ->where('model_id', $model->id)
                    ->when(!empty($onlyDealershipIds), fn($q) => $q->whereIn('dealership_id', $onlyDealershipIds))
                    ->count();
                $this->addPaginatedUrls($modelPath, $modelCount, $perPage);
            });
    }
}
