<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use Mtc\Crm\Contracts\FormActionModel;
use Mtc\MercuryDataModels\Audit;
use Mtc\MercuryDataModels\CarConfiguratorColour;
use Mtc\MercuryDataModels\CarConfiguratorCustomSection;
use Mtc\MercuryDataModels\CarConfiguratorEdition;
use Mtc\MercuryDataModels\CarConfiguratorEngine;
use Mtc\MercuryDataModels\CarConfiguratorInterior;
use Mtc\MercuryDataModels\CarConfiguratorRestriction;
use Mtc\MercuryDataModels\CarConfiguratorSection;
use Mtc\MercuryDataModels\CarConfiguratorTrim;
use Mtc\MercuryDataModels\CarConfiguratorWheel;
use Mtc\MercuryDataModels\Content;
use Mtc\MercuryDataModels\DealershipContent;
use Mtc\MercuryDataModels\DealershipDepartment;
use Mtc\MercuryDataModels\FormQuestion;
use Mtc\MercuryDataModels\FormSection;
use Mtc\MercuryDataModels\MenuEntry;
use Mtc\MercuryDataModels\NewCarContent;
use Mtc\MercuryDataModels\PropertyAttributeValue;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Mtc\MercuryDataModels\VehicleFeature;
use Mtc\MercuryDataModels\VehicleFinance;
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;
    private ?Collection $formSectionCache = null;
    private ?Collection $pageContentCache = 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(),
            'car-config-model' => $this->getCarConfigRelatedAuditables(),
            'vehicle' => $this->getVehicleAuditables(),
            'form' => $this->getFormRelatedAuditables(),
            'page' => [
                [
                    'type' => 'content',
                    'ids' => Content::query()
                        ->where('page_id', $this->auditableId)
                        ->pluck('id'),
                ],
            ],
            default => [],
        };
    }

    private function getVehicleAuditables(): array
    {
        return [
            [
                'type' => 'vehicle-finance',
                'ids' => VehicleFinance::query()
                    ->where('vehicle_id', $this->auditableId)
                    ->pluck('id'),
            ],
            ...$this->getVehicleRelatedAuditables('vehicle'),
        ];
    }

    private function getFormRelatedAuditables(): array
    {
        $sectionIds = FormSection::query()
            ->where('form_id', $this->auditableId)
            ->pluck('id');

        return [
            [
                'type' => 'form-section',
                'ids' => $sectionIds,
            ],
            [
                'type' => 'form-question',
                'ids' => FormQuestion::query()
                    ->where('form_id', $this->auditableId)
                    ->pluck('id'),
            ],
            [
                'type' => 'form-action',
                'ids' => app(FormActionModel::class)->newQuery()
                    ->where('form_id', $this->auditableId)
                    ->pluck('id'),
            ],
        ];
    }

    private function getCarConfigRelatedAuditables(): array
    {
        $byModel = fn (string $class) => $class::query()
            ->where('model_id', $this->auditableId)
            ->pluck('id');

        return [
            ['type' => 'car-config-section', 'ids' => $byModel(CarConfiguratorSection::class)],
            ['type' => 'car-config-custom-section', 'ids' => $byModel(CarConfiguratorCustomSection::class)],
            ['type' => 'car-config-engine', 'ids' => $byModel(CarConfiguratorEngine::class)],
            ['type' => 'car-config-trim', 'ids' => $byModel(CarConfiguratorTrim::class)],
            ['type' => 'car-config-colour', 'ids' => $byModel(CarConfiguratorColour::class)],
            ['type' => 'car-config-wheel', 'ids' => $byModel(CarConfiguratorWheel::class)],
            ['type' => 'car-config-interior', 'ids' => $byModel(CarConfiguratorInterior::class)],
            ['type' => 'car-config-edition', 'ids' => $byModel(CarConfiguratorEdition::class)],
            ['type' => 'car-config-restriction', 'ids' => $byModel(CarConfiguratorRestriction::class)],
        ];
    }

    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);
            } elseif (
                $this->auditableType === 'page'
                && $audit->auditable_type === 'content'
                && $audit->auditable
            ) {
                $path = $this->getPageContentPath($audit->auditable);
                $this->setNestedValue($mergedOld, $path, $oldValues);
                $this->setNestedValue($mergedNew, $path, $newValues);
            } elseif ($this->isFormChildAudit($audit) && $audit->auditable) {
                $path = $this->getFormChildPath($audit);
                $this->setNestedValue($mergedOld, $path, $oldValues);
                $this->setNestedValue($mergedNew, $path, $newValues);
            } elseif ($this->isCarConfigChildAudit($audit) && $audit->auditable) {
                $path = $this->getCarConfigChildPath($audit);
                $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 isFormChildAudit(Audit $audit): bool
    {
        return $this->auditableType === 'form'
            && in_array($audit->auditable_type, ['form-section', 'form-question', 'form-action'], true);
    }

    private function getFormChildPath(Audit $audit): array
    {
        $auditable = $audit->auditable;

        return match ($audit->auditable_type) {
            'form-section' => ['sections', $auditable->name ?: ('section #' . $auditable->id)],
            'form-question' => [
                'sections',
                $this->getFormSectionName($auditable->form_section_id),
                'questions',
                $auditable->name ?: ('question #' . $auditable->id),
            ],
            'form-action' => ['actions', $auditable->action_name ?: ('action #' . $auditable->id)],
            default => [],
        };
    }

    private function getFormSectionName(mixed $sectionId): string
    {
        if ($this->formSectionCache === null) {
            $this->formSectionCache = FormSection::query()
                ->where('form_id', $this->auditableId)
                ->get()
                ->keyBy('id');
        }

        $section = $this->formSectionCache->get($sectionId);

        return $section?->name ?: ('section #' . $sectionId);
    }

    private function isCarConfigChildAudit(Audit $audit): bool
    {
        return $this->auditableType === 'car-config-model'
            && in_array($audit->auditable_type, [
                'car-config-section',
                'car-config-custom-section',
                'car-config-engine',
                'car-config-trim',
                'car-config-colour',
                'car-config-wheel',
                'car-config-interior',
                'car-config-edition',
                'car-config-restriction',
            ], true);
    }

    private function getCarConfigChildPath(Audit $audit): array
    {
        $group = match ($audit->auditable_type) {
            'car-config-section' => 'sections',
            'car-config-custom-section' => 'custom',
            'car-config-engine' => 'engines',
            'car-config-trim' => 'trims',
            'car-config-colour' => 'colours',
            'car-config-wheel' => 'wheels',
            'car-config-interior' => 'interiors',
            'car-config-edition' => 'editions',
            'car-config-restriction' => 'restrictions',
        };

        $auditable = $audit->auditable;
        $label = $auditable->name ?? null;
        if (!$label && $audit->auditable_type === 'car-config-restriction') {
            $label = "{$auditable->functionality_type} #{$auditable->functionality_id}";
        }

        return [$group, $label ?: ('#' . $auditable->id)];
    }

    private function getPageContentPath(Content $content): array
    {
        if ($this->pageContentCache === null) {
            $this->pageContentCache = Content::query()
                ->where('page_id', $this->auditableId)
                ->get()
                ->keyBy('id');
        }

        $path = ['content'];
        $segments = [];
        $current = $content;

        while ($current) {
            array_unshift($segments, $current->name ?: ($current->slug ?: ('#' . $current->id)));
            $current = $current->parent_id
                ? $this->pageContentCache->get($current->parent_id)
                : null;
        }

        return array_merge($path, $segments);
    }

    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', 'vehicle-finance' => 'finance.',
            'dealership-department' => 'department.',
            'vehicle-feature' => 'feature.',
            'vehicle-attribute-value' => 'attribute.',
            'vehicle-trim' => 'trim.',
            (new VehicleTechnicalData())->getMorphClass() => 'spec.',
            (new VehicleStandardEquipment())->getMorphClass() => 'equipment.',
            default => '',
        };
    }
}
