<?php

namespace Maestro\Core\Http\Controllers;

use App\Services\Access\PermissionResolver;
use Illuminate\Routing\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class NavigationController extends Controller
{
    public function __construct(
        protected PermissionResolver $permissionResolver
    ) {}

    /**
     * Return navigation tree filtered by the authenticated user's permissions.
     * Frontend sends currentPath (query or X-Current-Path header). When currentPath is
     * /companies/{id}/..., companies.children are hydrated with that id; otherwise
     * companies.children are empty so no subsidebar appears on /companies index.
     */
    public function index(Request $request): JsonResponse
    {
        $user = $request->user();
        $navConfig = config('navigation', []);

        // Support legacy (config is array) or config with 'tree' key.
        $tree = isset($navConfig['tree']) ? $navConfig['tree'] : $navConfig;
        $currentPath = $request->query('currentPath') ?? $request->header('X-Current-Path');

        $companyId = null;
        $projectId = null;
        if ($currentPath && preg_match('#^/companies/(\d+)/projects/(\d+)(/|$)#', $currentPath, $m)) {
            $companyId = (int) $m[1];
            $projectId = (int) $m[2];
        } elseif ($currentPath && preg_match('#^/companies/(\d+)(/|$)#', $currentPath, $m)) {
            $companyId = (int) $m[1];
        }

        $prepared = $this->prepareTreeForPath($tree, $companyId, $projectId);
        $filtered = $this->filterTree($prepared, $user, $companyId, $projectId);

        // Guarantee exactly one root wrapper.
        $isSingleRoot = count($filtered) === 1
            && isset($filtered[0]['key'])
            && $filtered[0]['key'] === 'root';
        if (! $isSingleRoot) {
            $filtered = [
                [
                    'key' => 'root',
                    'label' => '',
                    'icon' => null,
                    'items' => $filtered,
                    'path' => '/dashboard',
                ],
            ];
        }

        // Hide empty headers in main tree.
        $root = $filtered[0];
        if (isset($root['items']) && is_array($root['items'])) {
            $items = $root['items'];
            $newItems = [];
            for ($i = 0; $i < count($items); $i++) {
                $node = $items[$i];
                if (! empty($node['header'])) {
                    $hasItemAfter = false;
                    for ($j = $i + 1; $j < count($items); $j++) {
                        if (! empty($items[$j]['header'])) {
                            break;
                        }
                        $hasItemAfter = true;
                        break;
                    }
                    if ($hasItemAfter) {
                        $newItems[] = $node;
                    }
                } else {
                    $newItems[] = $node;
                }
            }
            $filtered[0]['items'] = $newItems;
        }

        return response()->json($filtered);
    }

    /**
     * Prepare tree: when companyId is missing, clear companies.children so subsidebar does not show on /companies.
     * When companyId is present, hydrate companies.children routes by replacing {companyId}.
     * When both companyId and projectId are present, hydrate project-context routes by replacing {companyId} and {projectId}.
     */
    private function prepareTreeForPath(array $tree, ?int $companyId, ?int $projectId): array
    {
        $out = [];
        foreach ($tree as $node) {
            $node = $node;
            // Hydrate placeholders in this node's route/path at every level (e.g. nested company-settings.children).
            if ($companyId !== null) {
                if (isset($node['route']) && is_string($node['route'])) {
                    $node['route'] = str_replace('{companyId}', (string) $companyId, $node['route']);
                }
                if (isset($node['path']) && is_string($node['path'])) {
                    $node['path'] = str_replace('{companyId}', (string) $companyId, $node['path']);
                }
            }
            if ($projectId !== null) {
                if (isset($node['route']) && is_string($node['route'])) {
                    $node['route'] = str_replace('{projectId}', (string) $projectId, $node['route']);
                }
                if (isset($node['path']) && is_string($node['path'])) {
                    $node['path'] = str_replace('{projectId}', (string) $projectId, $node['path']);
                }
            }
            if (isset($node['key']) && $node['key'] === 'companies') {
                if ($companyId === null) {
                    $node['children'] = [];
                } else {
                    $replacer = fn (string $s): string => str_replace('{companyId}', (string) $companyId, $s);
                    $children = $node['children'] ?? [];
                    $node['children'] = array_map(function (array $child) use ($replacer): array {
                        if (isset($child['route'])) {
                            $child['route'] = $replacer($child['route']);
                        }
                        return $child;
                    }, $children);
                }
            }
            if (isset($node['key']) && $node['key'] === 'project-context') {
                if ($companyId !== null && $projectId !== null) {
                    $replacer = fn (string $s): string => str_replace(
                        ['{companyId}', '{projectId}'],
                        [(string) $companyId, (string) $projectId],
                        $s
                    );
                    $children = $node['children'] ?? [];
                    $node['children'] = array_map(function (array $child) use ($replacer): array {
                        if (isset($child['route'])) {
                            $child['route'] = $replacer($child['route']);
                        }
                        if (! empty($child['children'])) {
                            $child['children'] = array_map(function (array $grandChild) use ($replacer): array {
                                if (isset($grandChild['route'])) {
                                    $grandChild['route'] = $replacer($grandChild['route']);
                                }
                                return $grandChild;
                            }, $child['children']);
                        }
                        return $child;
                    }, $children);
                } else {
                    $node['children'] = [];
                }
            }
            if (! empty($node['children'])) {
                $node['children'] = $this->prepareTreeForPath($node['children'], $companyId, $projectId);
            }
            $out[] = $node;
        }
        return $out;
    }

    /**
     * Filter tree recursively by permission. Convert to frontend shape: route → to, children → items.
     * Header items (header: true) are never filtered out by route/children; they are passed through.
     *
     * When a node has scoped_permission and we have the matching scope (companyId for company.*,
     * projectId for project.*), visibility uses userHasPermission in that scope instead of module access.
     */
    private function filterTree(array $nodes, $user, ?int $companyId = null, ?int $projectId = null): array
    {
        $out = [];
        foreach ($nodes as $item) {
            $isHeader = ($item['header'] ?? false) === true;
            $permission = $item['permission'] ?? null;
            $scopedPermission = $item['scoped_permission'] ?? null;
            $children = $item['children'] ?? [];
            $filteredChildren = $this->filterTree($children, $user, $companyId, $projectId);

            $visible = true;
            if ($scopedPermission !== null) {
                if (str_starts_with($scopedPermission, 'company.') && $companyId !== null) {
                    $visible = $this->permissionResolver->userHasPermission($user, 'company', $companyId, $scopedPermission);
                } elseif (str_starts_with($scopedPermission, 'project.') && $projectId !== null) {
                    $visible = $this->permissionResolver->userHasPermission($user, 'project', $projectId, $scopedPermission);
                } else {
                    $visible = false;
                }
            } elseif ($permission === null) {
                $visible = true;
            } elseif (is_string($permission)) {
                $visible = $this->permissionResolver->userHasModuleAccess($user, $permission);
            } elseif (is_array($permission)) {
                $visible = false;
                foreach ($permission as $p) {
                    if ($this->permissionResolver->userHasModuleAccess($user, $p)) {
                        $visible = true;
                        break;
                    }
                }
            }

            if (! $visible) {
                continue;
            }

            $hasRoute = ! empty($item['route']) || ! empty($item['name']);
            $hadChildren = ! empty($children);
            // Hide parent when it has no visible children (e.g. don't show "Instellingen" when user has no access to any submenu item).
            if (! $isHeader && $hadChildren && empty($filteredChildren)) {
                continue;
            }
            if (! $isHeader && ! $hasRoute && empty($filteredChildren)) {
                continue;
            }

            $out[] = $this->toFrontendNode($item, $filteredChildren);
        }

        return $out;
    }

    private function toFrontendNode(array $item, array $filteredChildren): array
    {
        $node = [
            'label' => $item['label'] ?? '',
            'icon' => $item['icon'] ?? null,
            'items' => $filteredChildren,
        ];
        if (isset($item['key'])) {
            $node['key'] = $item['key'];
        }
        if (! empty($item['path'])) {
            $node['path'] = $item['path'];
        }
        if (! empty($item['route'])) {
            $node['to'] = $item['route'];
        }
        if (! empty($item['name'])) {
            $node['name'] = $item['name'];
        }
        if (isset($item['tooltip'])) {
            $node['tooltip'] = $item['tooltip'];
        }
        if (isset($item['header'])) {
            $node['header'] = $item['header'];
        }
        if (isset($item['pinnedBottom'])) {
            $node['pinnedBottom'] = $item['pinnedBottom'];
        }
        if (isset($item['description'])) {
            $node['description'] = $item['description'];
        }
        if (isset($item['visible'])) {
            $node['visible'] = $item['visible'];
        }
        // Expose required permission so frontend can filter by auth/me permissions (defense-in-depth).
        if (array_key_exists('permission', $item)) {
            $node['permission'] = $item['permission'];
        }
        return $node;
    }
}
