/* The business side, built for phones first: a slim top bar (with the shift and the offline / syncing state), the
   page, and a bottom tab bar within thumb reach (Sell, Sales, Customers, Products, More). On a computer the same
   tabs sit in the top bar. */
import { Head, Link, router, usePage } from '@inertiajs/react';
import { useEffect, useState, type ComponentType, type ReactNode } from 'react';
import {
    Activity, AlertTriangle, BarChart3, Boxes, ChevronRight, Clock, CloudOff, CreditCard, HandCoins, LogOut, Menu, MessageSquareText, Package, Receipt,
    RefreshCw, Settings, ShoppingCart, UserRound, Users, Wallet, X, type LucideProps,
} from 'lucide-react';
import type { SharedProps } from '../types';
import { cx } from '../components/ui';
import { FlashToast } from '../components/overlays';
import BusinessMark from '../components/BusinessMark';
import { clearOfflineCache, discard, flush, startOffline, useOffline } from '../offline';
import { useT } from '../i18n';

type Tab = { label: string; route: string; match?: string; icon: ComponentType<LucideProps> };

const TABS: Tab[] = [
    { label: 'Sell', route: 'sell', icon: ShoppingCart },
    { label: 'Sales', route: 'sales.index', match: 'sales.*', icon: Receipt },
    { label: 'Customers', route: 'customers.index', match: 'customers.*', icon: HandCoins },
    { label: 'Products', route: 'products.index', match: 'products.*', icon: Package },
];

function moreLinks(owner: boolean, loans: boolean, bales: boolean): Tab[] {
    return [
        // kaunjika keeps Products here, in case they sell a few counted items too
        ...(bales && owner ? [{ label: 'Products', route: 'products.index', match: 'products.*', icon: Package }] : []),
        { label: 'My shift', route: 'shifts.index', match: 'shifts.*', icon: Clock },
        { label: 'Expenses', route: 'expenses.index', icon: Wallet },
        ...(owner ? [
            { label: 'Profit and loss', route: 'reports', icon: BarChart3 },
            { label: 'Staff', route: 'team.index', icon: Users },
            // SMS reminders are for loans: only when loans are switched on
            ...(loans ? [{ label: 'SMS reminders', route: 'sms', icon: MessageSquareText }] : []),
            { label: 'Business settings', route: 'settings', icon: Settings },
            { label: 'Subscription', route: 'billing', icon: CreditCard },
            { label: 'Activity log', route: 'activity', icon: Activity },
        ] : []),
        { label: 'My profile and PIN', route: 'profile', icon: UserRound },
    ];
}

const isOn = (t: Tab) => route().current(t.route) || (!!t.match && route().current(t.match));

/** Trial ending, or payment due: the owner is told before anything locks. */
function BillingBar() {
    const { business } = usePage<SharedProps>().props;
    const b = business?.billing;
    if (!b || route().current('billing')) return null;
    const show = (b.status === 'trial' && (b.trialDaysLeft ?? 99) <= 7) || b.status === 'overdue';
    if (!show) return null;
    return (
        <Link href={route('billing')} className={cx('flex items-center gap-2 px-4 py-2 text-sm font-semibold', b.status === 'overdue' ? 'bg-rose-50 text-rose-800' : 'bg-amber-50 text-amber-900')}>
            <AlertTriangle className="size-4 shrink-0" aria-hidden />
            <span className="min-w-0 flex-1">
                {b.status === 'overdue'
                    ? `Subscription payment due. Pay ${b.fee} before ${b.locksOn} to keep selling.`
                    : b.trialDaysLeft === 0 ? 'Your free trial ends today.' : `Free trial: ${b.trialDaysLeft} day${b.trialDaysLeft === 1 ? '' : 's'} left.`}
            </span>
            <ChevronRight className="size-4 shrink-0" aria-hidden />
        </Link>
    );
}

/** Running out of prepaid SMS: the owner is told while there is still time to buy more. */
function SmsBar() {
    const { business } = usePage<SharedProps>().props;
    const left = business?.smsLeft;
    if (left === null || left === undefined || left > 3 || route().current('sms')) return null;
    return (
        <Link href={route('sms')} className={cx('flex items-center gap-2 px-4 py-2 text-sm font-semibold', left === 0 ? 'bg-rose-50 text-rose-800' : 'bg-amber-50 text-amber-900')}>
            <MessageSquareText className="size-4 shrink-0" aria-hidden />
            <span className="min-w-0 flex-1">{left === 0 ? 'No SMS left: reminders are not being sent. Buy more SMS.' : `Only ${left} SMS left. Buy more before they run out.`}</span>
            <ChevronRight className="size-4 shrink-0" aria-hidden />
        </Link>
    );
}

/** Offline, sending, or things the server refused: always visible, so nobody wonders whether a sale was kept. */
function SyncBar() {
    const t = useT();
    const { online, pending, syncing, failed } = useOffline();
    const [open, setOpen] = useState(false);
    if (online && !pending && !syncing && !failed.length) return null;
    return (
        <div className={cx('px-4 py-2 text-sm font-semibold', failed.length ? 'bg-rose-50 text-rose-800' : !online ? 'bg-ink text-white' : 'bg-sky-50 text-sky-900')}>
            <button onClick={() => (failed.length ? setOpen((o) => !o) : flush())} className="flex w-full items-center gap-2 text-left">
                {!online ? <CloudOff className="size-4 shrink-0" aria-hidden /> : <RefreshCw className={cx('size-4 shrink-0', syncing && 'animate-spin')} aria-hidden />}
                <span className="flex-1">
                    {!online ? t('Offline') : syncing ? t('Sending…') : ''}
                    {pending > 0 && ` · ${pending} ${t('waiting to send')}`}
                    {failed.length > 0 && ` · ${failed.length} ${t('not accepted')}`}
                </span>
            </button>
            {open && failed.length > 0 && (
                <ul className="mt-2 space-y-1.5">
                    {failed.map((f) => (
                        <li key={f.client_uuid} className="flex items-start gap-2 rounded-lg bg-white p-2 text-xs font-normal text-ink">
                            <span className="flex-1"><span className="font-bold">{f.label}</span><br />{f.error}</span>
                            <button onClick={() => discard(f.client_uuid)} className="font-bold text-rose-700">Remove</button>
                        </li>
                    ))}
                </ul>
            )}
        </div>
    );
}

export default function ShopLayout({ title, children, wide = false, bare = false }: { title: string; children: ReactNode; wide?: boolean; bare?: boolean }) {
    const { auth, business, shift, appName } = usePage<SharedProps>().props;
    const t = useT();
    const offline = useOffline();
    const [more, setMore] = useState(false);
    const owner = auth?.role === 'owner';
    const loans = !!business?.loansEnabled;
    // no Customers tab for a business that doesn't give goods on credit
    const bales = !!business?.balesEnabled;
    // kaunjika: Bales (owner) in place of Products; staff don't see either
    const tabs = TABS.filter((tab) => loans || tab.route !== 'customers.index')
        .flatMap((tab) => (tab.route === 'products.index' && bales ? (owner ? [{ label: 'Bales', route: 'bales.index', icon: Boxes }] : []) : [tab]));
    const links = moreLinks(owner, loans, bales);
    const moreOn = links.some(isOn);
    useEffect(() => router.on('navigate', () => setMore(false)), []);
    useEffect(() => { if (auth) startOffline(auth.id); }, [auth]);
    // the business's own colour, kept in step after it is changed in Settings
    useEffect(() => { if (business?.brandColor) document.documentElement.style.setProperty('--brand', business.brandColor); }, [business?.brandColor]);
    // offline, a page that was never opened can't load: say so quietly instead of showing an error box
    useEffect(() => router.on('invalid', (e) => { if (!navigator.onLine) e.preventDefault(); }), []);
    useEffect(() => router.on('exception', (e) => { if (!navigator.onLine) e.preventDefault(); }), []);

    const signOut = async () => {
        if (offline.pending && !confirm(`${offline.pending} item(s) on this phone are not sent yet. They will wait until you sign in again here. Sign out anyway?`)) return;
        await clearOfflineCache();
        router.post(route('logout'));
    };

    return (
        <>
            <Head title={`${t(title)} · ${business?.name ?? appName}`} />
            <FlashToast />
            <div className="flex min-h-screen flex-col">
                <header className="sticky top-0 z-30 border-b border-line bg-white/90 backdrop-blur print:hidden">
                    <div className={cx('mx-auto flex h-14 items-center gap-3 px-4', wide ? 'max-w-6xl' : 'max-w-3xl lg:max-w-5xl')}>
                        <BusinessMark logoUrl={business?.logoUrl ?? null} initials={business?.initials ?? 'DS'} />
                        <div className="min-w-0 flex-1">
                            <p className="truncate text-[15px] leading-tight font-bold text-ink">{business?.name ?? appName}</p>
                            <p className="truncate text-xs leading-tight text-muted">{auth?.name} · {auth?.roleLabel}</p>
                        </div>
                        {!bare && (business?.usesShifts || shift) && (
                            <Link href={route('shifts.index')} className={cx('flex shrink-0 items-center gap-1 rounded-full px-2.5 py-1 text-xs font-bold', shift ? 'bg-emerald-50 text-emerald-700' : 'bg-line-2 text-muted')}
                                title={shift ? `${t('Shift open since')} ${shift}` : t('No shift open')}>
                                <Clock className="size-3.5" aria-hidden />{shift ?? t('Shift')}
                            </Link>
                        )}
                        {!bare && (
                            <nav className="hidden items-center gap-1 md:flex" aria-label="Main">
                                {tabs.map((tab) => (
                                    <Link key={tab.route} href={route(tab.route)} className={cx('flex items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-semibold', isOn(tab) ? 'bg-brand/10 text-brand' : 'text-ink-2 hover:bg-line-2')}>
                                        <tab.icon className="size-4" aria-hidden />{t(tab.label)}
                                    </Link>
                                ))}
                            </nav>
                        )}
                        <button onClick={() => setMore(true)} className={cx('hidden items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-semibold md:flex', moreOn ? 'bg-brand/10 text-brand' : 'text-ink-2 hover:bg-line-2')}>
                            <Menu className="size-4" aria-hidden />{t('More')}
                        </button>
                    </div>
                    <SyncBar />
                    <BillingBar />
                    <SmsBar />
                </header>

                <main className={cx('mx-auto w-full flex-1 px-4 pt-4 pb-28 md:pb-10', wide ? 'max-w-6xl' : 'max-w-3xl lg:max-w-5xl')}>{children}</main>

                {!bare && (
                    <nav className="pb-safe fixed inset-x-0 bottom-0 z-30 border-t border-line bg-white md:hidden print:hidden" aria-label="Main">
                        <div className={cx('grid', tabs.length === 4 ? 'grid-cols-5' : tabs.length === 3 ? 'grid-cols-4' : 'grid-cols-3')}>
                            {tabs.map((tab) => {
                                const on = isOn(tab);
                                return (
                                    <Link key={tab.route} href={route(tab.route)} className={cx('flex flex-col items-center gap-0.5 py-2.5 text-[11px] font-bold', on ? 'text-brand' : 'text-muted')} aria-current={on ? 'page' : undefined}>
                                        <tab.icon className="size-6" strokeWidth={on ? 2.4 : 2} aria-hidden /><span className="max-w-full truncate px-0.5">{t(tab.label)}</span>
                                    </Link>
                                );
                            })}
                            <button onClick={() => setMore(true)} className={cx('flex flex-col items-center gap-0.5 py-2.5 text-[11px] font-bold', moreOn ? 'text-brand' : 'text-muted')}>
                                <Menu className="size-6" aria-hidden />{t('More')}
                            </button>
                        </div>
                    </nav>
                )}
            </div>

            {more && (
                <div className="fixed inset-0 z-40 flex items-end justify-center bg-ink/40 md:items-start md:justify-end md:p-4" onClick={() => setMore(false)}>
                    <div className="animate-sheet-in pb-safe max-h-[90vh] w-full max-w-md overflow-y-auto rounded-t-3xl bg-white p-3 shadow-pop md:mt-12 md:rounded-2xl" onClick={(e) => e.stopPropagation()} role="dialog" aria-label={t('More')}>
                        <div className="flex items-center justify-between px-3 pt-1 pb-2">
                            <p className="text-sm font-bold text-muted">{t('More')}</p>
                            <button onClick={() => setMore(false)} className="grid size-9 place-items-center rounded-full text-muted hover:bg-line-2" aria-label={t('Close')}><X className="size-5" /></button>
                        </div>
                        <ul>
                            {links.map((l) => (
                                <li key={l.route}>
                                    <Link href={route(l.route)} className={cx('flex items-center gap-3 rounded-xl px-3 py-3 text-base font-semibold', isOn(l) ? 'bg-brand/10 text-brand' : 'text-ink hover:bg-line-2')}>
                                        <l.icon className="size-5" aria-hidden /><span className="flex-1">{t(l.label)}</span><ChevronRight className="size-4 text-muted" aria-hidden />
                                    </Link>
                                </li>
                            ))}
                            <li>
                                <button onClick={signOut} className="flex w-full items-center gap-3 rounded-xl px-3 py-3 text-base font-semibold text-rose-700 hover:bg-rose-50">
                                    <LogOut className="size-5" aria-hidden />{t('Sign out')}
                                </button>
                            </li>
                        </ul>
                    </div>
                </div>
            )}
        </>
    );
}
