<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\Audit;
use Mtc\MercuryDataModels\MenuEntry;
use Mtc\MercuryDataModels\PropertyAttributeValue;

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);
            });

        $related = $this->getRelatedAuditableIds();

        if ($related !== null && $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 getRelatedAuditableIds(): ?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'),
            ],
            default => null,
        };
    }

    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') . '.',
            default => '',
        };
    }
}
