<?php

namespace App;

use App\Facades\Feature;
use App\Http\Resources\OfferTileListResource;
use App\Http\Resources\VehicleListResource;
use App\Services\GraphService;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\ResourceView;
use Mtc\MercuryDataModels\User;
use Mtc\MercuryDataModels\UserDashboardWidget;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleOffer;

class DashboardRepository
{
    protected static array $notForListingSites = [
        'popular-vehicles',
        'recent-vehicles'
    ];
    private Collection $userWidgets;

    public function __construct(private readonly GraphService $graphService, private readonly Request $request)
    {
    }

    public function load(User $user, string $section): array
    {
        if ($this->userHasSetWidgets($user, $section) === false) {
            $this->loadSectionDefaultLayout($section);
        }

        return $this->userWidgets
            ->filter(fn ($widgetData) => $this->isWidgetFeatureEnabled($widgetData))
            ->map(fn ($widgetData) => $this->mapData($widgetData))
            ->values()
            ->toArray();
    }

    private function isWidgetFeatureEnabled(UserDashboardWidget|array $widget): bool
    {
        $feature = config('dashboard.widgets.' . $widget['widget'] . '.feature');

        return empty($feature) || Feature::isEnabled($feature);
    }

    public function setForUser(User $user, array $widgets, string $section = 'home'): void
    {
        $user->dashboardWidgets()->delete();
        collect($widgets)
            ->filter(fn ($widget) => !empty($widget['widget']))
            ->each(fn ($widget, $index) => $user->dashboardWidgets()->create([
                'tenant_id' => tenant('id'),
                'section' => $section,
                'position' => $index,
                'kind' => $this->getWidgetKind($widget['widget']),
                'widget' => $widget['widget'],
                'data' => $widget['data'],
            ]));
    }

    public function getAvailable(User $user, string $section): array
    {
        //  in future filter by user roles and sections
        return collect(config('dashboard.widgets'))
            ->values()
            ->toArray();
    }

    private function userHasSetWidgets(User $user, string $section): bool
    {
        $this->userWidgets = $user->dashboardWidgets()
            ->where('section', $section)
            ->where('tenant_id', tenant('id'))
            ->get()
            ->keyBy('position');

        return $this->userWidgets->isNotEmpty();
    }

    private function loadSectionDefaultLayout(string $section)
    {
        match ($section) {
            'home' => $this->userWidgets = collect(config('dashboard.default_dashboard.home'))
                ->reject(fn ($item) => tenant('type') === 'listing'
                    && in_array($item['widget'], self::$notForListingSites))
                ->values(),
            default => $this->userWidgets = collect(),
        };
    }

    public function vehiclesByView(Carbon $from, Carbon $to, int $limit = 5): array
    {
        return Vehicle::query()
            ->select(['id', 'title', 'derivative', 'registration_number'])
            ->addSelect(['view_count' => $this->viewCountSubQuery('vehicle', (new Vehicle())->getTable(), $from, $to)])
            ->active()
            ->with('mediaUses.media')
            ->orderByDesc('view_count')
            ->limit($limit)
            ->get()
            ->toArray();
    }

    public function vehiclesByEnquiries(Carbon $from, Carbon $to, int $limit = 5): array
    {
        return Vehicle::query()
            ->select(['id', 'title', 'derivative', 'registration_number'])
            ->addSelect(['enquiry_count' => $this->enquiryCountSubQuery('vehicle', (new Vehicle())->getTable(), $from, $to)])
            ->active()
            ->with('mediaUses.media')
            ->orderByDesc('enquiry_count')
            ->limit($limit)
            ->get()
            ->toArray();
    }

    public function popularVehicles(Carbon $from, Carbon $to): array
    {
        return [
            'views' => $this->vehiclesByView($from, $to),
            'enquiries' => $this->vehiclesByEnquiries($from, $to),
        ];
    }

    public function offersByView(Carbon $from, Carbon $to, int $limit = 5): array
    {
        return VehicleOffer::query()
            ->select(['id', 'name', 'derivative'])
            ->addSelect(['view_count' => $this->viewCountSubQuery('offer', (new VehicleOffer())->getTable(), $from, $to)])
            ->active()
            ->with('mediaUses.media')
            ->orderByDesc('view_count')
            ->limit($limit)
            ->get()
            ->toArray();
    }

    public function offersByEnquiries(Carbon $from, Carbon $to, int $limit = 5): array
    {
        return VehicleOffer::query()
            ->select(['id', 'name', 'derivative'])
            ->addSelect(['enquiry_count' => $this->enquiryCountSubQuery('offer', (new VehicleOffer())->getTable(), $from, $to)])
            ->active()
            ->with('mediaUses.media')
            ->orderByDesc('enquiry_count')
            ->limit($limit)
            ->get()
            ->toArray();
    }

    /**
     * New cars ordered by enquiry count. New car views are not tracked, so only enquiries apply.
     */
    public function newCarsByEnquiries(Carbon $from, Carbon $to, int $limit = 5): array
    {
        return NewCar::query()
            ->select(['id', 'name', 'derivative', 'make_id'])
            ->addSelect(['enquiry_count' => $this->enquiryCountSubQuery('new-car', (new NewCar())->getTable(), $from, $to)])
            ->active()
            ->with(['mediaUses.media', 'make:id,name'])
            ->orderByDesc('enquiry_count')
            ->limit($limit)
            ->get()
            ->toArray();
    }

    public function popularOffers(Carbon $from, Carbon $to): array
    {
        return [
            'views' => $this->offersByView($from, $to),
            'enquiries' => $this->offersByEnquiries($from, $to),
        ];
    }

    public function popularNewCars(Carbon $from, Carbon $to): array
    {
        return [
            'enquiries' => $this->newCarsByEnquiries($from, $to),
        ];
    }

    /**
     * Popular segments for a date range, feature-gated to match the dashboard section widgets.
     */
    public function popularData(Carbon $from, Carbon $to): array
    {
        $data = ['vehicles' => $this->popularVehicles($from, $to)];

        if (Feature::isEnabled('new-vehicles')) {
            $data['offers'] = $this->popularOffers($from, $to);
            $data['new_cars'] = $this->popularNewCars($from, $to);
        }

        return $data;
    }

    /**
     * Sum of resource-view hits for a model within the date range.
     */
    private function viewCountSubQuery(string $viewableType, string $table, Carbon $from, Carbon $to): Builder
    {
        return ResourceView::query()
            ->select(DB::raw('COALESCE(SUM(hits), 0)'))
            ->where('viewable_type', $viewableType)
            ->whereColumn('viewable_id', $table . '.id')
            ->whereBetween('date', [$from->copy()->startOfDay(), $to->copy()->endOfDay()]);
    }

    /**
     * Count enquiries linked to a model via the enquiry `objects` relation within the date range.
     * Mirrors Vehicle::scopeWithEnquiryCount (object_type comes from the enquiry object map).
     */
    private function enquiryCountSubQuery(string $objectType, string $table, Carbon $from, Carbon $to): Builder
    {
        $enquiryModel = config('crm.enquiry_model');

        return $enquiryModel::query()
            ->select(DB::raw('COUNT(*)'))
            ->whereHas('objects', fn ($object) => $object
                ->where('object_type', $objectType)
                ->whereColumn('object_id', $table . '.id'))
            ->whereBetween('ingested_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()]);
    }

    public function mapData(UserDashboardWidget|array $widget)
    {
        $widget['data'] = match ($widget['widget']) {
            // Popular segments are loaded separately (date-range aware) via the dashboard popular endpoint.
            'popular-vehicles', 'popular-offers', 'popular-new-cars' => [],
            'chart' => $this->loadChartData($widget['data']),
            'recent-vehicles', 'recent-offers' => $this->loadSection($widget['widget'], $widget['data'])
                ->toArray($this->request),
            default => $this->loadKPI($widget['widget'], $widget['data'])
        };
        return $widget;
    }

    public function loadChartData(mixed $data): array
    {
        return $this->graphService->getGraph($data);
    }

    private function loadSection(mixed $widget, mixed $data): JsonResource
    {
        return match ($widget) {
            'recent-offers' => new OfferTileListResource(
                VehicleOffer::query()
                    ->active()
                    ->with([
                        'make',
                        'model',
                        'mediaUses.media',
                    ])
                    ->latest()
                    ->take(5)
                    ->get()
            )->setThumbDimensions('square'),
            'recent-vehicles' => new VehicleListResource(
                Vehicle::query()
                    ->active()
                    ->with('mediaUses.media')
                    ->latest()
                    ->take(5)
                    ->get()
            ),
        };
    }

    private function loadKPI(mixed $widget, mixed $data)
    {
        $widgetClass = config('reports.kpis.' . $widget);
        return $widgetClass ? App::make($widgetClass)->toArray() : [];
    }

    private function getWidgetKind(mixed $widget): string
    {
        return config("dashboard.widgets.$widget.kind");
    }
}
