<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\Audit;
use Mtc\MercuryDataModels\DealershipContent;
use Mtc\MercuryDataModels\DealershipDepartment;
use Mtc\MercuryDataModels\MenuEntry;
use Mtc\MercuryDataModels\NewCarContent;
use Mtc\MercuryDataModels\PropertyAttributeValue;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Mtc\MercuryDataModels\VehicleFeature;
use Mtc\MercuryDataModels\VehicleOfferContent;
use Mtc\MercuryDataModels\VehicleOfferFinance;
use Mtc\MercuryDataModels\VehicleStandardEquipment;
use Mtc\MercuryDataModels\VehicleTechnicalData;
use Mtc\MercuryDataModels\VehicleTrim;

class AuditableResource extends JsonResource
{
    public static $wrap = '';

    private string $auditableType;
    private mixed $auditableId;
    private ?Collection $menuEntryCache = null;

    public function __construct(string $auditableType, mixed $auditableId)
    {
        $this->auditableType = $auditableType;
        $this->auditableId = $auditableId;
        parent::__construct(null);
    }

    public function toArray($request): array
    {
        $audits = $this->fetchAudits()
            ->groupBy(fn(Audit $audit) => $audit->user_id . '_' . $audit->created_at->format('Y-m-d H:i:s'))
            ->map(fn($group) => $this->mergeAuditGroup($group))
            ->values()
            ->take(50);

        return [
            'audits' => $audits,
        ];
    }

    private function fetchAudits(): Collection
    {
        $query = Audit::query()
            ->where(function ($q) {
                $q->where('auditable_type', $this->auditableType)
                    ->where('auditable_id', $this->auditableId);
            });

        foreach ($this->getRelatedAuditables() as $related) {
            if ($related['ids']->isNotEmpty()) {
                $query->orWhere(function ($q) use ($related) {
                    $q->where('auditable_type', $related['type'])
                        ->whereIn('auditable_id', $related['ids']);
                });
            }
        }

        return $query
            ->with(['user', 'auditable'])
            ->orderByDesc('created_at')
            ->limit(100)
            ->get();
    }

    private function getRelatedAuditables(): array
    {
        return match ($this->auditableType) {
            'property' => [
                [
                    'type' => 'property-attribute',
                    'ids' => PropertyAttributeValue::query()
                        ->where('property_id', $this->auditableId)
                        ->pluck('id'),
                ],
            ],
            'menu' => [
                [
                    'type' => 'menu_entry',
                    'ids' => MenuEntry::query()
                        ->where('menu_id', $this->auditableId)
                        ->pluck('id'),
                ],
            ],
            'offer' => $this->getOfferRelatedAuditables(),
            'new-car' => $this->getNewCarRelatedAuditables(),
            'dealership' => $this->getDealershipRelatedAuditables(),
            default => [],
        };
    }

    private function getDealershipRelatedAuditables(): array
    {
        return [
            [
                'type' => 'dealership-content',
                'ids' => DealershipContent::query()
                    ->where('dealership_id', $this->auditableId)
                    ->pluck('id'),
            ],
            [
                'type' => 'dealership-department',
                'ids' => DealershipDepartment::query()
                    ->where('dealership_id', $this->auditableId)
                    ->pluck('id'),
            ],
            [
                'type' => 'vehicle-attribute-value',
                'ids' => VehicleAttributeValue::query()
                    ->where('owner_id', $this->auditableId)
                    ->where('owner_type', 'dealership')
                    ->pluck('id'),
            ],
        ];
    }

    private function getVehicleRelatedAuditables(string $vehicleType): array
    {
        return [
            [
                'type' => 'vehicle-feature',
                'ids' => VehicleFeature::query()
                    ->where('vehicle_id', $this->auditableId)
                    ->where('vehicle_type', $vehicleType)
                    ->pluck('id'),
            ],
            [
                'type' => 'vehicle-attribute-value',
                'ids' => VehicleAttributeValue::query()
                    ->where('owner_id', $this->auditableId)
                    ->where('owner_type', $vehicleType)
                    ->pluck('id'),
            ],
            [
                'type' => (new VehicleTechnicalData())->getMorphClass(),
                'ids' => VehicleTechnicalData::query()
                    ->where('vehicle_id', $this->auditableId)
                    ->where('vehicle_type', $vehicleType)
                    ->pluck('id'),
            ],
            [
                'type' => (new VehicleStandardEquipment())->getMorphClass(),
                'ids' => VehicleStandardEquipment::query()
                    ->where('vehicle_id', $this->auditableId)
                    ->where('vehicle_type', $vehicleType)
                    ->pluck('id'),
            ],
        ];
    }

    private function getOfferRelatedAuditables(): array
    {
        return [
            [
                'type' => 'offer-content',
                'ids' => VehicleOfferContent::query()
                    ->where('offer_id', $this->auditableId)
                    ->pluck('id'),
            ],
            [
                'type' => 'offer-finance',
                'ids' => VehicleOfferFinance::query()
                    ->where('offer_id', $this->auditableId)
                    ->pluck('id'),
            ],
            ...$this->getVehicleRelatedAuditables('offer'),
        ];
    }

    private function getNewCarRelatedAuditables(): array
    {
        return [
            [
                'type' => 'new-car-content',
                'ids' => NewCarContent::query()
                    ->where('car_id', $this->auditableId)
                    ->pluck('id'),
            ],
            [
                'type' => 'vehicle-trim',
                'ids' => VehicleTrim::query()
                    ->where('new_car_id', $this->auditableId)
                    ->pluck('id'),
            ],
            ...$this->getVehicleRelatedAuditables('new-car'),
        ];
    }

    private function mergeAuditGroup($group): array
    {
        $first = $group->first();
        $mergedOld = [];
        $mergedNew = [];

        foreach ($group as $audit) {
            $oldValues = is_string($audit->old_values)
                ? json_decode($audit->old_values, true) ?? []
                : ($audit->old_values ?? []);
            $newValues = is_string($audit->new_values)
                ? json_decode($audit->new_values, true) ?? []
                : ($audit->new_values ?? []);

            if ($audit->auditable_type === 'menu_entry' && $audit->auditable) {
                $path = $this->getMenuEntryPath($audit->auditable);
                $this->setNestedValue($mergedOld, $path, $oldValues);
                $this->setNestedValue($mergedNew, $path, $newValues);
            } else {
                $prefix = $this->getAuditPrefix($audit);
                foreach ($oldValues as $key => $value) {
                    $mergedOld[$prefix . $key] = $value;
                }
                foreach ($newValues as $key => $value) {
                    $mergedNew[$prefix . $key] = $value;
                }
            }
        }

        return [
            'id' => $first->id,
            'event' => $first->event,
            'created_at' => $first->created_at->format('d/m/Y H:i'),
            'user_name' => $first->user?->name ?? 'System',
            'old_values' => $mergedOld,
            'new_values' => $mergedNew,
        ];
    }

    private function getMenuEntryPath(MenuEntry $entry): array
    {
        if ($this->menuEntryCache === null) {
            $this->menuEntryCache = MenuEntry::query()
                ->where('menu_id', $this->auditableId)
                ->get()
                ->keyBy('id');
        }

        $path = [];
        $current = $entry;

        while ($current) {
            array_unshift($path, $current->name ?? 'entry');
            $current = $current->parent_id
                ? $this->menuEntryCache->get($current->parent_id)
                : null;
        }

        return $path;
    }

    private function setNestedValue(array &$target, array $path, array $values): void
    {
        $ref = &$target;

        foreach ($path as $segment) {
            if (!isset($ref[$segment]) || !is_array($ref[$segment])) {
                $ref[$segment] = [];
            }
            $ref = &$ref[$segment];
        }

        $ref = array_merge($ref, $values);
    }

    private function getAuditPrefix(Audit $audit): string
    {
        return match ($audit->auditable_type) {
            'property-attribute' => ($audit->auditable?->slug ?? 'attr') . '.',
            'offer-content', 'new-car-content', 'dealership-content' => 'content.',
            'offer-finance' => 'finance.',
            'dealership-department' => 'department.',
            'vehicle-feature' => 'feature.',
            'vehicle-attribute-value' => 'attribute.',
            'vehicle-trim' => 'trim.',
            (new VehicleTechnicalData())->getMorphClass() => 'spec.',
            (new VehicleStandardEquipment())->getMorphClass() => 'equipment.',
            default => '',
        };
    }
}
