import { ReactNode, useCallback, useState } from 'react';

import {
    Box, createTheme, CssBaseline, Drawer, Grid, Link, List, ListItemButton,
    ListItemIcon, ListItemText, ListSubheader, Stack, ThemeProvider, Toolbar,
} from "@mui/material";
import { indigo, lime } from "@mui/material/colors";
import { useQuery } from "@tanstack/react-query";

import DashboardIcon from "@mui/icons-material/Dashboard";
import ImageIcon from "@mui/icons-material/Image";
import VideocamIcon from "@mui/icons-material/Videocam";
import PhotoCameraIcon from "@mui/icons-material/PhotoCamera";
import SwitchCameraIcon from "@mui/icons-material/SwitchCamera";
import GridOnIcon from "@mui/icons-material/GridOn";
import BoltIcon from "@mui/icons-material/Bolt";
import SaveIcon from "@mui/icons-material/Save";
import WindPowerIcon from "@mui/icons-material/WindPower";
import StraightenIcon from "@mui/icons-material/Straighten";
import DeveloperBoardIcon from "@mui/icons-material/DeveloperBoard";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import ApiIcon from "@mui/icons-material/Api";
import MenuBookIcon from "@mui/icons-material/MenuBook";
import HistoryIcon from "@mui/icons-material/History";
import CodeIcon from "@mui/icons-material/Code";
import GavelIcon from "@mui/icons-material/Gavel";
import _ from "lodash";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";

import StatusBar from "./components/StatusBar";
import ErrorMessage from "./components/ErrorMessage";
import DataProcessingPlots from "./components/DataProcessingPlots";
import MeasurementStatistics from "./components/MeasurementStatistics";
import DataProcessingSettings from "./components/DataProcessingSettings";
import PreviewImage from "./components/PreviewImage";
import DataCollection from "./components/DataCollection";
import DetectorSettings from "./components/DetectorSettings";
import DetectorSelection from "./components/DetectorSelection";
import DetectorStatus from "./components/DetectorStatus";
import PixelMask from "./components/PixelMask";
import MaskVisualization from "./components/MaskVisualization";
import DarkMaskSettings from "./components/DarkMaskSettings";
import IndexingSettings from "./components/IndexingSettings";
import BraggIntegrationSettings from "./components/BraggIntegrationSettings";
import AzIntSettings from "./components/AzIntSettings";
import ROI from "./components/ROI";
import FileWriterSettings from "./components/FileWriterSettings";
import ImageFormatSettings from "./components/ImageFormatSettings";
import InstrumentMetadata from "./components/InstrumentMetadata";
import ZeroMQPreview from "./components/ZeroMQPreview";
import ImagePusherStatus from "./components/ImagePusherStatus";
import Calibration from "./components/Calibration";
import FpgaStatus from "./components/FpgaStatus";

import { plot_type } from "./client";
import { getStatisticsOptions } from "./client/@tanstack/react-query.gen";
import { JFJOCH_VERSION } from "./version";
// Bundled at build time, so they always match the running release.
import changelogMd from "../../docs/CHANGELOG.md?raw";
import licenseMd from "../../docs/LICENSE.md?raw";

const drawerWidth = 240;

// Pale indigo drawer that echoes the indigo top bar without repeating it, with a
// lime accent on the selected entry (lime reads well small, garish large).
const drawerBg = indigo[50];
const drawerSelectedBg = indigo[100];

// PSI branding sits alone in the footer credit bar with clear space (never merged
// with the app wordmark or menu icon). The four official logos differ only in the
// diffused spots; pick one at module load so it is fixed per page load and never
// re-randomises on render. BASE_URL keeps the path correct under the /frontend/ base.
// These are the positive (dark) logos, used as-is on the light footer.
const PSI_LOGOS = [1, 2, 3, 4].map(i => `${import.meta.env.BASE_URL}psi_0${i}.png`);
const PSI_LOGO = PSI_LOGOS[Math.floor(Math.random() * PSI_LOGOS.length)];

const jfjoch_theme = createTheme({
    palette: {
        primary: indigo,
        secondary: lime,
    },
    // Slightly rounder corners than the MUI default (4px) give panels, inputs and
    // buttons a softer, more modern feel without changing any component's behaviour.
    shape: { borderRadius: 8 },
});

/** Flow a page's settings panels into responsive columns (masonry) so a page of
 *  small panels reads wide-and-short instead of as one tall single-file column.
 *  Panels never split across a column break. */
function PanelColumns({ columns = 2, children }: { columns?: number; children: ReactNode }) {
    return (
        <Box sx={{
            columnCount: { xs: 1, md: columns },
            columnGap: 2,
            '& > *': { breakInside: 'avoid', mb: 2, display: 'block' },
        }}>
            {children}
        </Box>
    );
}

// An item either renders a panel inside the app (render) or links out to an
// external page in a new tab (href), e.g. the source repository.
type NavItem = { id: string; label: string; icon: ReactNode; disabled?: boolean;
                 href?: string; render?: () => ReactNode };
type NavSection = { subheader?: string; items: NavItem[] };

/** Documentation / API reference shown inside the app area, with a new-tab escape hatch. */
function DocFrame({ title, src }: { title: string; src: string }) {
    return (
        <Box>
            <Box sx={{ mb: 1, textAlign: "right" }}>
                <Link href={src} target="_blank" rel="noopener"
                      sx={{ display: "inline-flex", alignItems: "center", gap: 0.5 }}>
                    Open in new tab <OpenInNewIcon fontSize="small"/>
                </Link>
            </Box>
            <Box component="iframe" title={title} src={src}
                 sx={{ width: "100%", height: "calc(100vh - 220px)", border: "1px solid",
                       borderColor: "divider", borderRadius: 1 }}/>
        </Box>
    );
}

/** Render bundled Markdown inline, styled with the app theme so it reads as part of
 *  the frontend rather than the framed Sphinx documentation. */
function MarkdownDoc({ md }: { md: string }) {
    return (
        <Box sx={{
            maxWidth: 900, mx: "auto",
            "& h1": { typography: "h4", mt: 0, mb: 2 },
            "& h2": { typography: "h5", mt: 4, mb: 1, color: "primary.dark" },
            "& h3": { typography: "h6", mt: 3, mb: 1 },
            "& ul": { pl: 3, my: 1 },
            "& li": { mb: 0.5 },
            "& a": { color: "primary.main" },
            "& code": { bgcolor: "grey.100", px: 0.5, borderRadius: 0.5, fontSize: "0.85em" },
        }}>
            <Markdown remarkPlugins={[remarkGfm]}>{md}</Markdown>
        </Box>
    );
}

function App() {
    const [drawerOpen, setDrawerOpen] = useState(true);
    const [view, setView] = useState('dashboard');

    const toggleDrawer = useCallback(() => setDrawerOpen(o => !o), []);

    // Poll general statistics once per second; structural sharing keeps unchanged
    // slices referentially stable so memoized panels only re-render when their own
    // slice changes.
    const { data, isError, refetch } = useQuery({
        ...getStatisticsOptions(),
        refetchInterval: 1000,
    });
    const s = isError || data === undefined ? {} : data;
    const isMeasuring = s.broker?.state === 'Measuring';

    // Every panel that used to live behind a toggle is now a single drawer entry;
    // only the active one is mounted, so the screen shows one thing at a time.
    const sections: NavSection[] = [
        { items: [
            { id: 'dashboard', label: 'Dashboard', icon: <DashboardIcon/>, render: () => (
                <Grid container spacing={3} justifyContent="center" alignItems="flex-start">
                    <Grid item xs={12} lg={8}>
                        <DataProcessingPlots height={550} type={plot_type.BKG_ESTIMATE}/>
                    </Grid>
                    <Grid item xs={12} lg={4}>
                        <MeasurementStatistics s={s.measurement ?? {}}/>
                    </Grid>
                    <Grid item xs={12} lg={8}>
                        <DataProcessingPlots height={800} type={plot_type.INDEXING_RATE}/>
                    </Grid>
                    <Grid item xs={12} lg={4}>
                        <DataProcessingSettings s={s.data_processing_settings} update={refetch}/>
                    </Grid>
                </Grid>
            )},
        ]},
        { subheader: 'Acquisition', items: [
            { id: 'preview', label: 'Live preview', icon: <ImageIcon/>, render: () => (
                <PreviewImage measuring={isMeasuring}
                              max_image_number={s.buffer?.max_image_number}
                              min_image_number={s.buffer?.min_image_number}/>
            )},
            { id: 'data-collection', label: 'Data collection', icon: <VideocamIcon/>, render: () => (
                <DataCollection frame_time_us={s.detector_settings?.frame_time_us ?? 500}/>
            )},
        ]},
        { subheader: 'Expert', items: [
            { id: 'detector', label: 'Detector', icon: <PhotoCameraIcon/>, render: () => (
                <Stack spacing={2}>
                    <DetectorStatus s={s.detector}/>
                    <DetectorSettings s={s.detector_settings}/>
                </Stack>
            )},
            { id: 'detector-selection', label: 'Detector selection', icon: <SwitchCameraIcon/>, render: () => (
                <DetectorSelection s={s.detector_list}/>
            )},
            { id: 'masks', label: 'Masks', icon: <GridOnIcon/>, render: () => (
                <Stack spacing={2}>
                    <PanelColumns>
                        <PixelMask s={s.pixel_mask}/>
                        <DarkMaskSettings s={s.dark_mask}/>
                    </PanelColumns>
                    <MaskVisualization/>
                </Stack>
            )},
            { id: 'processing', label: 'On-the-fly processing', icon: <BoltIcon/>, render: () => (
                <Stack spacing={2}>
                    <PanelColumns>
                        <IndexingSettings s={s.indexing} status={s.broker}/>
                        <BraggIntegrationSettings s={s.bragg_integration}/>
                        <AzIntSettings s={s.az_int}/>
                    </PanelColumns>
                    {/* ROI holds three wide side-by-side tables — keep it full width. */}
                    <ROI s={s.roi}/>
                </Stack>
            )},
            { id: 'output', label: 'Output', icon: <SaveIcon/>, render: () => (
                <PanelColumns>
                    <FileWriterSettings s={s.file_writer_settings}/>
                    <ImageFormatSettings s={s.image_format_settings}/>
                    <InstrumentMetadata s={s.instrument_metadata}/>
                </PanelColumns>
            )},
            { id: 'streaming', label: 'Streaming', icon: <WindPowerIcon/>, render: () => (
                <PanelColumns>
                    <ZeroMQPreview s={s.zeromq_preview}/>
                    <ImagePusherStatus s={s.image_pusher}/>
                </PanelColumns>
            )},
            { id: 'calibration', label: 'JUNGFRAU calibration', icon: <StraightenIcon/>,
              disabled: _.isEmpty(s.calibration), render: () => (
                <Calibration s={s.calibration}/>
            )},
            { id: 'fpga', label: 'FPGA status', icon: <DeveloperBoardIcon/>,
              disabled: _.isEmpty(s.fpga), render: () => (
                <FpgaStatus s={s.fpga}/>
            )},
        ]},
        { subheader: 'Reference', items: [
            { id: 'api-reference', label: 'API reference', icon: <ApiIcon/>, render: () => (
                <DocFrame title="API reference" src="/frontend/openapi.html"/>
            )},
            { id: 'documentation', label: 'Documentation', icon: <MenuBookIcon/>, render: () => (
                <DocFrame title="Documentation" src="/frontend/docs/index.html"/>
            )},
            { id: 'changelog', label: 'Changelog', icon: <HistoryIcon/>, render: () => (
                <MarkdownDoc md={changelogMd}/>
            )},
            { id: 'license', label: 'License', icon: <GavelIcon/>, render: () => (
                <MarkdownDoc md={licenseMd}/>
            )},
            { id: 'source-code', label: 'Source code', icon: <CodeIcon/>,
              href: 'https://gitea.psi.ch/mx/jungfraujoch' },
        ]},
    ];

    const activeItem = sections.flatMap(sec => sec.items).find(it => it.id === view) ?? sections[0].items[0];

    return <ThemeProvider theme={jfjoch_theme}>
        <CssBaseline enableColorScheme/>
        <Box sx={{ display: 'flex' }}>
            <StatusBar s={s.broker} onMenuClick={toggleDrawer}/>
            {drawerOpen && (
                <Drawer variant="permanent"
                        sx={{ width: drawerWidth, flexShrink: 0,
                              '& .MuiDrawer-paper': { width: drawerWidth, boxSizing: 'border-box',
                                                      bgcolor: drawerBg, borderRight: 'none' } }}>
                    <Toolbar/>
                    <Box sx={{ overflow: 'auto', pb: 9 }}>
                        {sections.map((sec, i) => (
                            <List key={i} subheader={sec.subheader
                                ? <ListSubheader sx={{ bgcolor: 'transparent', color: 'primary.dark',
                                                       fontWeight: 600 }}>{sec.subheader}</ListSubheader>
                                : undefined}>
                                {sec.items.map(it => (
                                    <ListItemButton key={it.id}
                                                    disabled={it.disabled}
                                                    {...(it.href
                                                        ? { component: 'a', href: it.href,
                                                            target: '_blank', rel: 'noopener' }
                                                        : { selected: view === it.id,
                                                            onClick: () => setView(it.id) })}
                                                    sx={{ borderLeft: '4px solid transparent',
                                                          '&.Mui-selected': { bgcolor: drawerSelectedBg,
                                                              borderColor: 'secondary.main' },
                                                          '&.Mui-selected:hover': { bgcolor: drawerSelectedBg } }}>
                                        <ListItemIcon sx={{ minWidth: 40 }}>{it.icon}</ListItemIcon>
                                        <ListItemText primary={it.label}/>
                                    </ListItemButton>
                                ))}
                            </List>
                        ))}
                    </Box>
                </Drawer>
            )}
            <Box component="main" sx={{ flexGrow: 1, p: 3, pb: '84px', minWidth: 0 }}>
                <Toolbar/>
                <ErrorMessage s={s.broker}/>
                <Box sx={{ maxWidth: 1300, mx: 'auto', mt: 2 }}>
                    {activeItem.render?.()}
                </Box>
            </Box>
        </Box>
        <Box component="footer" sx={{
            position: 'fixed', bottom: 0, left: 0, right: 0,
            zIndex: (t) => t.zIndex.drawer + 1,
            bgcolor: 'background.paper', borderTop: '1px solid', borderColor: 'divider',
            py: 1.5, px: 2, textAlign: 'center', fontSize: '0.8rem',
        }}>
            <Link href="https://www.psi.ch" target="_blank" rel="noopener" aria-label="Paul Scherrer Institute"
                  sx={{ position: 'absolute', left: 24, top: '50%', transform: 'translateY(-50%)',
                        display: 'inline-flex' }}>
                <Box component="img" src={PSI_LOGO} alt="PSI"
                     sx={{ height: 34, width: 'auto', opacity: 0.85 }}/>
            </Link>
            Developed at{' '}
            <Link href="https://www.psi.ch" target="_blank" rel="noopener">Paul Scherrer Institute</Link>
            {' '}(2019–2026). Main author:{' '}
            <Link href="mailto:filip.leonarski@psi.ch">Filip Leonarski</Link> ·{' '}
            <Link href="https://doi.org/10.1107/S1600577522010268"><i>J. Synchrotron Rad.</i> (2023). <b>30</b>, 227–234</Link>
            {' '}· Version: {JFJOCH_VERSION}
        </Box>
    </ThemeProvider>
}

export default App;
