<?php

namespace Mtc\Modules\SiteMenu\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use CMSNav;
use Illuminate\Http\Request;
use Mtc\Modules\SiteMenu\Models\SiteMenuItem;
use Mtc\Shop\Category;
use Mtc\Shop\Item;

/**
 * Site Menu admin. A single Twig shell (index) mounts a Vue 2 SPA; every screen
 * (manage / add-edit / trash) is a Vue view and every request is served by the
 * JSON API methods below. Renders inside layouts/admin.twig for chrome + auth.
 */
class SiteMenuController extends Controller
{
    /* ------------------------------------------------------------------ *
     *  Shell
     * ------------------------------------------------------------------ */

    /**
     * Render the SPA shell. All data is fetched client-side from the API.
     */
    public function index()
    {
        return template('admin/site_menu/index.twig', [
            'title'          => 'Manage Site Menu',
            'is_super_admin' => $this->isSuperAdmin(),
            'brands_enabled' => defined('BRANDS_ENABLED') && BRANDS_ENABLED === true,
            'cms_enabled'    => defined('CMS_ENABLED') && CMS_ENABLED === true,
            'categories_max' => defined('CATEGORIES_MAX') ? CATEGORIES_MAX : 0,
            'csrf'           => csrf_token(),
        ]);
    }

    /**
     * The former Add/Edit and Trash pages are now Vue views inside the shell;
     * keep the routes as redirects for old bookmarks and the no-JS save path.
     */
    public function edit()
    {
        return redirect()->route('admin-site-menu');
    }

    public function trash()
    {
        return redirect()->route('admin-site-menu');
    }

    /* ------------------------------------------------------------------ *
     *  JSON API
     * ------------------------------------------------------------------ */

    /**
     * Items for a menu level (root by default), with a breadcrumb trail.
     */
    public function apiItems(Request $request)
    {
        $id = $request->input('parent_id');
        $current = !empty($id) ? SiteMenuItem::find($id) : SiteMenuItem::find(1);
        if (!$current) {
            $current = SiteMenuItem::find(1);
        }

        $items = $current ? $current->getSubMenu(true) : collect();

        return response()->json([
            'current'     => $current ? ['id' => $current->id, 'name' => $current->name] : null,
            'breadcrumbs' => $current ? $this->breadcrumbTrail($current) : [],
            'items'       => $items->map(function ($item) {
                return [
                    'id'               => $item->id,
                    'name'             => $item->name,
                    'published'        => (bool) $item->published,
                    'child_count'      => $item->directChildrenCount(),
                    'children_preview' => $item->children()->orderBy('order')->take(6)->pluck('name'),
                ];
            })->values(),
        ]);
    }

    /**
     * Bootstrap data for the add/edit form.
     */
    public function apiForm(Request $request, $id = null)
    {
        if (!empty($id)) {
            $add_edit = 'Edit';
            $menu = SiteMenuItem::find($id);
        } else {
            $add_edit = 'Add';
            $menu = new SiteMenuItem();
            if (!empty($request->input('parent_id'))) {
                $menu->parent_id = (int) $request->input('parent_id');
            }
        }

        if (!$menu) {
            return response()->json(['error' => 'Menu item not found.'], 404);
        }

        return response()->json([
            'add_edit'       => $add_edit,
            'menu'           => [
                'id'              => $menu->id,
                'name'            => $menu->name,
                'published'       => (bool) $menu->published,
                'parent_id'       => (int) ($menu->parent_id ?? 0),
                'url'             => $menu->url,
                'target'          => $menu->target ?: '_self',
                'css_class'       => $menu->css_class,
                'single_cms_page' => (int) $menu->single_cms_page,
                'mega_menu'       => (bool) $menu->mega_menu,
                'mega_menu_items' => $this->hydrateMegaItems($menu->mega_menu_items),
            ],
            'parent_options' => SiteMenuItem::getAllSiteMenuOptions($menu->parent_id),
            'is_super_admin' => $this->isSuperAdmin(),
            'brands_enabled' => defined('BRANDS_ENABLED') && BRANDS_ENABLED === true,
            'cms_enabled'    => defined('CMS_ENABLED') && CMS_ENABLED === true,
            'categories_max' => defined('CATEGORIES_MAX') ? CATEGORIES_MAX : 0,
            'brand_list'     => (int) $menu->brand_list,
            'category_list'  => (int) $menu->category_list,
            'options'        => [
                'category_id'     => $this->categoryOptions($menu->category_id),
                'single_category' => $this->parentCategoryOptions($menu->single_category),
                'category_list'   => $this->categoryOptions($menu->category_list),
                'product_list'    => $this->categoryOptions($menu->product_list),
                'cms_tree'        => $this->cmsTreeOptions($menu->site_tree),
            ],
        ]);
    }

    /**
     * Soft-deleted entries for the Trash view.
     */
    public function apiTrash()
    {
        return response()->json([
            'items' => SiteMenuItem::onlyTrashed()->orderBy('name')->get()
                ->map(fn ($m) => ['id' => $m->id, 'name' => $m->name])->values(),
        ]);
    }

    /**
     * Persist a new sibling order. Body: { order: [id, id, ...] }.
     * Uses a query-builder update (no model events) so cache is reset manually.
     */
    public function reorder(Request $request)
    {
        $order = (array) $request->input('order', []);
        foreach (array_values($order) as $position => $itemId) {
            SiteMenuItem::where('id', (int) $itemId)->update(['order' => $position]);
        }
        SiteMenuItem::resetCache();

        return response()->json(['success' => true]);
    }

    /* ------------------------------------------------------------------ *
     *  Mutations (JSON on wantsJson, redirect fallback for no-JS)
     * ------------------------------------------------------------------ */

    /**
     * Persist an add/edit.
     */
    public function store(Request $request)
    {
        $data = $request->all();

        if (empty($data['name'])) {
            if ($request->wantsJson()) {
                return response()->json(['errors' => ['name' => 'Menu item name is required']], 422);
            }
            return redirect()->back()->withInput()
                ->with('site_menu_error', 'Menu item name is required');
        }

        $id = $request->input('id');
        $is_add = empty($id);
        $menu = $is_add ? new SiteMenuItem() : SiteMenuItem::find($id);

        if (!$menu) {
            if ($request->wantsJson()) {
                return response()->json(['errors' => ['general' => 'Menu item not found.']], 404);
            }
            return redirect()->route('admin-site-menu')
                ->with('site_menu_error', 'Menu item not found.');
        }

        // Decode the mega-menu picker payload into an array before fill() so the
        // model's array cast encodes it exactly once (assigning the raw JSON
        // string would double-encode). Store only the {type, id} refs.
        $data['mega_menu'] = $request->boolean('mega_menu') ? 1 : 0;
        $mega_items = json_decode($request->input('mega_menu_items', '[]'), true);
        $data['mega_menu_items'] = collect(is_array($mega_items) ? $mega_items : [])
            ->filter(fn ($ref) => in_array($ref['type'] ?? null, ['category', 'product'], true) && !empty($ref['id']))
            ->map(fn ($ref) => ['type' => $ref['type'], 'id' => (int) $ref['id']])
            ->values()
            ->all();

        $menu->fill($data);
        $menu->css_class = trim(strip_tags((string) $menu->css_class));
        $menu->url = trim(strip_tags((string) $menu->url));
        $menu->target = trim(strip_tags((string) $menu->target)) ?: '_self';

        if (!empty($menu->url)) {
            $parsed_url = parse_url($menu->url);
            if (!array_key_exists('host', $parsed_url)) {
                $menu->url = '/' . ltrim($menu->url, '/');
            }
        }

        $menu->save();

        if ($is_add) {
            $menu->order = $menu->id;
            $menu->save();
        }

        $message = $is_add ? 'Menu added' : 'Menu updated';

        if ($request->wantsJson()) {
            return response()->json(['success' => true, 'message' => $message, 'id' => $menu->id]);
        }

        return redirect()->route('admin-site-menu')->with('site_menu_message', $message);
    }

    /**
     * Soft-delete a menu entry (and, via the model, its children).
     */
    public function destroy(Request $request, $id)
    {
        $parent_id = 0;
        $menu = SiteMenuItem::find($id);
        if ($menu) {
            $parent_id = (int) $menu->parent_id;
            SiteMenuItem::destroy($id);
        }

        if ($request->wantsJson()) {
            return response()->json(['success' => (bool) $menu]);
        }

        return redirect()->route('admin-site-menu', $parent_id ? ['id' => $parent_id] : [])
            ->with('site_menu_message', 'Menu item deleted');
    }

    /**
     * Toggle a menu entry's published flag.
     */
    public function changePublished(Request $request, $id, $value)
    {
        $menu = SiteMenuItem::find($id);
        if ($menu && in_array((int) $value, [0, 1], true)) {
            $menu->published = (int) $value;
            $menu->save();
        }

        if ($request->wantsJson()) {
            return response()->json([
                'success'   => (bool) $menu,
                'published' => (bool) ($menu->published ?? false),
            ]);
        }

        $back = $menu && $menu->parent_id ? ['id' => (int) $menu->parent_id] : [];

        return redirect()->route('admin-site-menu', $back);
    }

    /**
     * Deep-clone a menu entry with all descendants.
     */
    public function duplicate(Request $request, $id)
    {
        $menu = SiteMenuItem::find($id);
        if ($menu) {
            $menu->replicateWithRelations();
        }

        if ($request->wantsJson()) {
            return response()->json(['success' => (bool) $menu]);
        }

        $back = $menu && $menu->parent_id ? ['id' => (int) $menu->parent_id] : [];

        return redirect()->route('admin-site-menu', $back);
    }

    /**
     * Restore a soft-deleted entry.
     */
    public function restore(Request $request, $id)
    {
        $menu = SiteMenuItem::onlyTrashed()->find($id);
        if ($menu) {
            $menu->restore();
        }

        if ($request->wantsJson()) {
            return response()->json(['success' => (bool) $menu]);
        }

        return redirect()->route('admin-site-menu');
    }

    /* ------------------------------------------------------------------ *
     *  Helpers
     * ------------------------------------------------------------------ */

    /**
     * Root-to-node breadcrumb trail ([{id,name}, ...]) for drilling.
     */
    protected function breadcrumbTrail($menu): array
    {
        $trail = [];
        foreach ($menu->getAncestors()->reverse() as $ancestor) {
            $trail[] = ['id' => $ancestor->id, 'name' => $ancestor->name];
        }
        $trail[] = ['id' => $menu->id, 'name' => $menu->name];

        return $trail;
    }

    protected function isSuperAdmin(): bool
    {
        return defined('MTCADMIN_USERID') && ($_SESSION['adminId'] ?? null) == MTCADMIN_USERID;
    }

    /**
     * Combined category + product search for the mega-menu picker.
     * Returns active matches as [{type, id, name, url}], categories first.
     */
    public function apiSearch(Request $request)
    {
        $term = trim((string) $request->input('term', ''));

        if (mb_strlen($term) < 2) {
            return response()->json(['results' => []]);
        }

        // Escape LIKE wildcards so a literal % or _ can't widen the match.
        $like = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $term) . '%';
        $results = [];

        foreach (Category::active()->where('name', 'like', $like)->orderBy('name')->limit(20)->get() as $category) {
            $results[] = [
                'type' => 'category',
                'id'   => (int) $category->id,
                'name' => $category->name,
                'url'  => browse_url($category->id),
            ];
        }

        foreach (Item::query()->active()->where('name', 'like', $like)->orderBy('name')->limit(20)->get() as $product) {
            $results[] = [
                'type' => 'product',
                'id'   => (int) $product->id,
                'name' => $product->name,
                'url'  => $product->url,
            ];
        }

        return response()->json(['results' => $results]);
    }

    /**
     * Resolve stored mega-menu refs ([{type, id}]) into display objects
     * ([{type, id, name, url}]) so the picker can show already-selected items
     * when editing. Skips refs that no longer resolve.
     */
    protected function hydrateMegaItems($refs): array
    {
        $refs = is_array($refs) ? $refs : [];
        $items = [];

        foreach ($refs as $ref) {
            $type = $ref['type'] ?? null;
            $id = (int) ($ref['id'] ?? 0);

            if (!$id) {
                continue;
            }

            if ($type === 'category') {
                $category = Category::find($id);
                if ($category) {
                    $items[] = ['type' => 'category', 'id' => $id, 'name' => $category->name, 'url' => browse_url($id)];
                }
            } elseif ($type === 'product') {
                $product = Item::find($id);
                if ($product) {
                    $items[] = ['type' => 'product', 'id' => $id, 'name' => $product->name, 'url' => $product->url];
                }
            }
        }

        return $items;
    }

    /**
     * Build <option> HTML for the category selects (mirrors get_the_categories).
     */
    protected function categoryOptions($selected): string
    {
        if (!function_exists('get_the_categories')) {
            return '';
        }
        return (string) get_the_categories($selected, true, []);
    }

    /**
     * Build <option> HTML for the single-parent-category select.
     */
    protected function parentCategoryOptions($selected): string
    {
        if (!class_exists(Category::class) || !method_exists(Category::class, 'getParentCategories')) {
            return '';
        }

        $html = '';
        foreach (Category::getParentCategories() as $category) {
            $sel = ($selected == $category->id) ? ' selected' : '';
            $html .= '<option value="' . (int) $category->id . '"' . $sel . '>'
                . htmlspecialchars($category->name) . '</option>';
        }

        return $html;
    }

    /**
     * Build nested <option> HTML for the CMS tree select (3 levels deep).
     */
    protected function cmsTreeOptions($selected): string
    {
        if (!class_exists(CMSNav::class)) {
            return '';
        }

        $html = '';
        foreach (CMSNav::tree(0) as $id => $leaf) {
            $html .= $this->cmsOption($id, $leaf['title'], 0, $selected);
            foreach ($leaf['sub'] ?? [] as $l2_id => $l2) {
                $html .= $this->cmsOption($l2_id, $l2['title'], 1, $selected);
                foreach ($l2['sub'] ?? [] as $l3_id => $l3) {
                    $html .= $this->cmsOption($l3_id, $l3['title'], 2, $selected);
                }
            }
        }

        return $html;
    }

    private function cmsOption($id, $title, int $depth, $selected): string
    {
        $prefix = str_repeat('- ', $depth);
        $sel = ($selected == $id) ? ' selected="selected"' : '';
        return '<option value="' . (int) $id . '"' . $sel . '>' . $prefix . htmlspecialchars((string) $title) . '</option>';
    }
}
