<?php

class Menu {

    /**
     * Resolve a menu item URL by preferred labels within a menu slug.
     * Useful for linking static template buttons to Menu Builder-managed URLs.
     */
    public static function getItemUrl(string $slug, array $labels, string $fallback = '#'): string {
        if (empty($labels)) return $fallback;

        $rows = Db::select("
            SELECT mi.label, mi.url
            FROM sp_menu_items mi
            JOIN sp_menus m ON m.menu_id = mi.menu_id
            WHERE m.slug = ? AND mi.active = 1 AND m.active = 1
              AND mi.type = 'link' AND mi.url IS NOT NULL
        ", [$slug]);

        if (!$rows) return $fallback;

        $urlByLabel = [];
        foreach ($rows as $row) {
            $key = strtolower(trim((string)$row['label']));
            $urlByLabel[$key] = (string)$row['url'];
        }

        foreach ($labels as $label) {
            $key = strtolower(trim((string)$label));
            if (isset($urlByLabel[$key]) && trim($urlByLabel[$key]) !== '') {
                return $urlByLabel[$key];
            }
        }

        return $fallback;
    }

    /**
     * Returns the full nested menu tree for the given slug,
     * filtered by the current user's permissions (loaded from their role).
     */
    public static function getTree(string $slug): array {
        $rows = Db::select("
            SELECT mi.*, p.perm_controller, p.perm_action
            FROM sp_menu_items mi
            JOIN sp_menus m ON m.menu_id = mi.menu_id
            LEFT JOIN sp_permissions p ON p.perm_id = mi.perm_id
            WHERE m.slug = ? AND mi.active = 1 AND m.active = 1
            ORDER BY mi.sort_order ASC
        ", [$slug]);

        if (!$rows) return [];

        $isSysAdmin = !empty($_SESSION['login']['sysadmin']);
        if (!$isSysAdmin) {
            $userPerms = $_SESSION['permissions'] ?? [];
            $rows = array_values(array_filter($rows, function($item) use ($userPerms) {
                if (!$item['perm_id']) return true;
                $key = $item['perm_controller'] . '.' . $item['perm_action'];
                return in_array($key, $userPerms);
            }));
        }

        $currentUrl = strtok($_SERVER['REQUEST_URI'], '?');
        return self::buildTree($rows, $currentUrl);
    }

    /**
     * Resolves dynamic URL tokens for the logged-in user.
     *
     * Supported tokens (use in the menu builder URL field):
     *   %ORG_TOKEN%  → encrypted slug of the user's company  → /company/%ORG_TOKEN%
     *   %USER_TOKEN% → encrypted slug of the logged-in user  → /account/%USER_TOKEN%/edit
     */
    private static function resolveUrlTokens(string $url): string {
        if (strpos($url, '%') === false) return $url;

        if (strpos($url, '%ORG_TOKEN%') !== false) {
            $orgId = $_SESSION['login']['orgid'] ?? 0;
            if ($orgId) {
                $token = Functions::encryptData((string)$orgId, HASH_PASSWORD_KEY);
                $url   = str_replace('%ORG_TOKEN%', 'org.' . $token, $url);
            }
        }

        if (strpos($url, '%USER_TOKEN%') !== false) {
            $userId = $_SESSION['login']['userid'] ?? 0;
            if ($userId) {
                $token = Functions::encryptData((string)$userId, HASH_PASSWORD_KEY);
                $url   = str_replace('%USER_TOKEN%', 'user.' . $token, $url);
            }
        }

        return $url;
    }

    private static function buildTree(array $rows, string $currentUrl, int $parentId = 0): array {
        $branch = [];
        foreach ($rows as $row) {
            if ((int)$row['parent_id'] === $parentId) {
                $row['children'] = self::buildTree($rows, $currentUrl, (int)$row['item_id']);

                $rawUrl  = $row['url'] ?? '';
                $itemUrl = rtrim(self::resolveUrlTokens($rawUrl), '/');
                $row['url'] = $itemUrl; // write resolved URL back for the renderer
                $curUrl  = rtrim($currentUrl, '/');

                // URLs containing dynamic tokens (e.g. %ORG_TOKEN%) are encrypted with a
                // random IV, so the resolved URL differs on every request and can never
                // equal the token already present in the browser's address bar.
                // Instead, match on the static path segment that precedes the token.
                if (strpos($rawUrl, '%') !== false) {
                    $staticPrefix = rtrim(preg_replace('/%[^%]+%.*$/', '', $rawUrl), '/');
                    $row['is_active'] = $staticPrefix !== '' && (
                        $curUrl === $staticPrefix ||
                        str_starts_with($curUrl, $staticPrefix . '/')
                    );
                } else {
                    $row['is_active'] = $itemUrl !== '' && $curUrl === $itemUrl;
                }

                if (!empty($row['children'])) {
                    $row['is_open'] = $row['is_active'] || self::hasActiveChild($row['children']);
                } elseif ($row['match_prefix'] && $itemUrl !== '') {
                    $row['is_active'] = str_starts_with($currentUrl, $itemUrl);
                    $row['is_open']   = $row['is_active'];
                } else {
                    $row['is_open'] = false;
                }

                // Skip section headers with no visible children
                if ($row['type'] === 'header' && empty($row['children'])) continue;

                $branch[] = $row;
            }
        }
        return $branch;
    }

    private static function hasActiveChild(array $children): bool {
        foreach ($children as $child) {
            if ($child['is_active'] || $child['is_open']) return true;
        }
        return false;
    }
}
