import AppNav from '@/Components/AppNav';
import { cn } from '@/lib/cn';
import { Link, usePage } from '@inertiajs/react';
import {
    BarChart3,
    CreditCard,
    FileEdit,
    FileText,
    Landmark,
    Layers,
    LayoutTemplate,
    type LucideIcon,
    Menu,
    Palette,
    ScrollText,
    Settings as SettingsIcon,
    ShieldCheck,
    Users as UsersIcon,
    Webhook,
    Wrench,
    X,
} from 'lucide-react';
import {
    createContext,
    useContext,
    useEffect,
    useRef,
    useState,
    type ReactNode,
} from 'react';

// ---------------------------------------------------------------------------
// Mobile-menu context — AdminPageHeader (rendered inside children) needs to
// trigger the drawer, which lives in AdminLayout. Context bridges the two
// without prop-drilling across the persistent-layout boundary.
// ---------------------------------------------------------------------------
const AdminMobileMenuCtx = createContext<() => void>(() => {});

/**
 * Page-level header — title, optional description, optional right-side
 * actions, and the mobile sidebar toggle. Render this as the first element
 * inside a page that uses AdminLayout as its persistent layout.
 *
 * @example
 * ```tsx
 * function Dashboard() {
 *   return <>
 *     <AdminPageHeader title="Dashboard" />
 *     {content}
 *   </>
 * }
 * Dashboard.layout = (page: ReactNode) => <AdminLayout>{page}</AdminLayout>
 * ```
 */
export function AdminPageHeader({
    title,
    description,
    actions,
}: {
    title?: ReactNode;
    description?: ReactNode;
    actions?: ReactNode;
}) {
    const openMenu = useContext(AdminMobileMenuCtx);
    return (
        <div className="mb-6 flex flex-wrap items-start justify-between gap-3">
            <div className="flex items-start gap-2">
                <button
                    type="button"
                    onClick={openMenu}
                    aria-label="Open menu"
                    className="-ml-1 mt-0.5 rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden"
                    data-testid="admin-sidebar-toggle"
                >
                    <Menu className="h-4 w-4" strokeWidth={2} />
                </button>
                <div>
                    {title && (
                        <h1 className="text-xl font-semibold tracking-tight text-foreground sm:text-2xl">
                            {title}
                        </h1>
                    )}
                    {description && (
                        <p className="mt-1 text-sm text-muted-foreground">
                            {description}
                        </p>
                    )}
                </div>
            </div>
            {actions && (
                <div className="flex items-center gap-2">{actions}</div>
            )}
        </div>
    );
}

type NavGroup = {
    label: string;
    items: NavItem[];
};

type NavItem = {
    href: string;
    label: string;
    icon: LucideIcon;
    matchPrefix: string;
    badge?: string | number;
    /** Saas-only items don't render in self_hosted mode (single-user
     *  install — Tenants/Subscriptions/Invoices/Plans/etc would all be
     *  empty noise). */
    saasOnly?: boolean;
};

type Props = {
    children: ReactNode;
};

const NAV_GROUPS: NavGroup[] = [
    {
        label: 'Overview',
        items: [
            { href: '/admin', label: 'Dashboard', icon: BarChart3, matchPrefix: '/admin$', saasOnly: true },
        ],
    },
    {
        label: 'People',
        items: [
            { href: '/admin/users', label: 'Users', icon: UsersIcon, matchPrefix: '/admin/users' },
            { href: '/admin/tenants', label: 'Tenants', icon: ShieldCheck, matchPrefix: '/admin/tenants', saasOnly: true },
        ],
    },
    {
        label: 'Billing',
        items: [
            { href: '/admin/subscriptions', label: 'Subscriptions', icon: CreditCard, matchPrefix: '/admin/subscriptions', saasOnly: true },
            { href: '/admin/invoices', label: 'Invoices', icon: FileText, matchPrefix: '/admin/invoices', saasOnly: true },
            { href: '/admin/billing', label: 'Plans', icon: Layers, matchPrefix: '/admin/billing', saasOnly: true },
            { href: '/admin/settings?tab=gateways', label: 'Payment gateways', icon: Landmark, matchPrefix: '/admin/settings/gateways', saasOnly: true },
            { href: '/admin/webhook-events', label: 'Webhook events', icon: Webhook, matchPrefix: '/admin/webhook-events', saasOnly: true },
        ],
    },
    {
        label: 'Marketing',
        items: [
            { href: '/admin/kits', label: 'Kits', icon: LayoutTemplate, matchPrefix: '/admin/kits' },
            { href: '/admin/marketing', label: 'Pages', icon: FileEdit, matchPrefix: '/admin/marketing' },
        ],
    },
    {
        label: 'System',
        items: [
            { href: '/admin/settings', label: 'Settings', icon: SettingsIcon, matchPrefix: '/admin/settings' },
            { href: '/admin/appearance', label: 'Appearance', icon: Palette, matchPrefix: '/admin/appearance' },
            { href: '/admin/maintenance', label: 'Maintenance', icon: Wrench, matchPrefix: '/admin/maintenance' },
            { href: '/admin/audit-log', label: 'Audit log', icon: ScrollText, matchPrefix: '/admin/audit-log' },
        ],
    },
];

function isActive(currentUrl: string, item: NavItem): boolean {
    if (item.matchPrefix.endsWith('$')) {
        const exact = item.matchPrefix.slice(0, -1);
        return currentUrl === exact || currentUrl === exact + '/';
    }
    return currentUrl === item.matchPrefix || currentUrl.startsWith(item.matchPrefix + '/') || currentUrl.startsWith(item.matchPrefix + '?');
}

export default function AdminLayout({ children }: Props) {
    const { url, props } = usePage();
    const path = url.split('?')[0] ?? url;
    const [mobileOpen, setMobileOpen] = useState(false);
    const sidebarScrollRef = useRef<HTMLDivElement>(null);

    // In self_hosted mode, hide saas-only nav items entirely (Tenants,
    // Subscriptions, Invoices, Plans, Webhook events). They'd 404 on
    // click anyway and are noise for single-user installs.
    const isSelfHosted = (props as { appMode?: string }).appMode === 'self_hosted';
    const navGroups = NAV_GROUPS
        .map((group) => ({
            ...group,
            items: group.items.filter((item) => ! (isSelfHosted && item.saasOnly)),
        }))
        .filter((group) => group.items.length > 0);

    // Auto-close the mobile drawer on URL change so navigating doesn't leave
    // the overlay covering the destination page. Also ensure the active nav
    // link is visible inside the sidebar scroll container.
    useEffect(() => {
        setMobileOpen(false);
        const container = sidebarScrollRef.current;
        if (!container) return;
        const activeLink = container.querySelector<HTMLElement>('[aria-current="page"]');
        if (!activeLink) return;
        const linkBottom = activeLink.offsetTop + activeLink.offsetHeight;
        const viewBottom = container.scrollTop + container.clientHeight;
        if (linkBottom > viewBottom) {
            container.scrollTop = linkBottom - container.clientHeight + 8;
        } else if (activeLink.offsetTop < container.scrollTop) {
            container.scrollTop = activeLink.offsetTop - 8;
        }
    }, [url]);

    const sidebar = (collapseOnNav: boolean) => (
        <nav className="flex flex-col gap-5 p-4 lg:py-6">
            <div className="flex items-center justify-between gap-2 px-2">
                <div>
                    <span className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
                        Admin
                    </span>
                    <h2 className="mt-1 text-base font-semibold tracking-tight text-foreground">
                        Operations
                    </h2>
                </div>
                {collapseOnNav && (
                    <button
                        type="button"
                        onClick={() => setMobileOpen(false)}
                        aria-label="Close menu"
                        className="rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden"
                    >
                        <X className="h-4 w-4" strokeWidth={2} />
                    </button>
                )}
            </div>
            {navGroups.map((group) => (
                <div key={group.label} className="flex flex-col gap-1">
                    <span className="px-2 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70">
                        {group.label}
                    </span>
                    <ul className="flex flex-col gap-0.5">
                        {group.items.map((item) => {
                            const Icon = item.icon;
                            const active = isActive(path, item);
                            return (
                                <li key={item.href}>
                                    <Link
                                        href={item.href}
                                        data-testid={`admin-nav-${item.href.replace(/^\/admin\/?/, '') || 'dashboard'}`}
                                        aria-current={active ? 'page' : undefined}
                                        className={cn(
                                            'flex items-center gap-2 rounded-md px-2 py-1.5 text-[13px] transition-colors',
                                            active
                                                ? 'bg-accent/10 text-accent'
                                                : 'text-foreground/80 hover:bg-muted hover:text-foreground',
                                        )}
                                    >
                                        <Icon className="h-3.5 w-3.5" strokeWidth={2} />
                                        <span className="flex-1">{item.label}</span>
                                        {item.badge !== undefined && item.badge !== '' && (
                                            <span className="rounded-full bg-muted px-1.5 py-px text-[10px] font-medium text-foreground/70">
                                                {item.badge}
                                            </span>
                                        )}
                                    </Link>
                                </li>
                            );
                        })}
                    </ul>
                </div>
            ))}
        </nav>
    );

    return (
        <div className="flex min-h-screen flex-col bg-background text-foreground">
            <AppNav />
            <div className="flex flex-1 flex-col lg:flex-row">
                {/* Desktop sidebar — sticky to viewport, scrolls independently */}
                <aside
                    className="hidden shrink-0 border-r border-border bg-card lg:block lg:w-64"
                    data-testid="admin-sidebar"
                >
                    <div ref={sidebarScrollRef} className="sticky top-0 max-h-[calc(100vh-3.5rem)] overflow-y-auto">
                        {sidebar(false)}
                    </div>
                </aside>

                {/* Mobile drawer */}
                {mobileOpen && (
                    <div
                        className="fixed inset-0 z-40 bg-black/40 lg:hidden"
                        onClick={() => setMobileOpen(false)}
                        aria-hidden="true"
                    />
                )}
                <aside
                    className={cn(
                        'fixed inset-y-0 left-0 z-50 w-72 max-w-[80vw] transform overflow-y-auto border-r border-border bg-card transition-transform duration-200 lg:hidden',
                        mobileOpen ? 'translate-x-0' : '-translate-x-full',
                    )}
                    aria-hidden={!mobileOpen}
                    data-testid="admin-sidebar-mobile"
                >
                    {sidebar(true)}
                </aside>

                {/* Opacity-only entrance (NOT fade-in-up): a `transform` here
                    makes <main> the containing block for `position: fixed`
                    descendants, trapping every admin page's inline `fixed
                    inset-0` modal (Plans / Settings / price+currency) inside the
                    content column instead of the viewport. Keep the fade, drop
                    the translate. */}
                <AdminMobileMenuCtx.Provider value={() => setMobileOpen(true)}>
                    <main className="min-w-0 flex-1 px-4 py-6 sm:px-6 sm:py-8">
                        <div key={path} className="animate-fade-in">
                            {children}
                        </div>
                    </main>
                </AdminMobileMenuCtx.Provider>
            </div>
        </div>
    );
}

/**
 * Pre-typed persistent layout for admin pages. Assign to `ComponentName.layout`
 * instead of writing the arrow inline — avoids a per-page `ReactNode` import.
 *
 * @example
 * ```tsx
 * MyPage.layout = adminLayout;
 * export default MyPage;
 * ```
 */
export const adminLayout = (page: ReactNode) => <AdminLayout>{page}</AdminLayout>;
