import React, { useState } from "react";
import {
    Dialog, DialogTitle, DialogContent, DialogActions, Button,
    Grid, Typography, Link, Box, Tab, Tabs, IconButton, Chip, FormControlLabel, Checkbox,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import DeleteIcon from "@mui/icons-material/Delete";
import RefreshIcon from "@mui/icons-material/Refresh";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import BuildLogsTab from "components/package/BuildLogsTab";
import ChangesTab from "components/package/ChangesTab";
import EventsTab from "components/package/EventsTab";
import AutoRefreshControl from "components/common/AutoRefreshControl";
import { useRepository } from "hooks/useRepository";
import { useAuth } from "hooks/useAuth";
import { useNotification } from "hooks/useNotification";
import { useAutoRefresh } from "hooks/useAutoRefresh";
import { Client } from "api/client/AhrimanClient";
import { ApiError } from "api/client/ApiError";
import { QueryKeys } from "api/QueryKeys";
import { StatusHeaderStyles } from "theme/status/StatusHeaderStyles";
import { formatTimestamp } from "components/common/formatTimestamp";
import type { AutoRefreshInterval } from "api/types/AutoRefreshInterval";
import type { Dependencies } from "api/types/Dependencies";
import type { PackageProperties } from "api/types/PackageProperties";
import type { PackageStatus } from "api/types/PackageStatus";
import type { Patch } from "api/types/Patch";
import type { RepositoryId } from "api/types/RepositoryId";

interface PackageInfoDialogProps {
    packageBase: string | null;
    open: boolean;
    onClose: () => void;
    autorefreshIntervals: AutoRefreshInterval[];
}

function listToString(items: string[]): React.ReactNode {
    const unique = [...new Set(items)].sort();
    return unique.map((item, i) => (
        <React.Fragment key={item}>
            {item}
            {i < unique.length - 1 && <br />}
        </React.Fragment>
    ));
}

export default function PackageInfoDialog({ packageBase, open, onClose, autorefreshIntervals }: PackageInfoDialogProps): React.JSX.Element {
    const { current } = useRepository();
    const { enabled: authEnabled, username } = useAuth();
    const { showSuccess, showError } = useNotification();
    const queryClient = useQueryClient();
    const hasAuth = !authEnabled || username !== null;

    const [tabIndex, setTabIndex] = useState(0);
    const [refreshDb, setRefreshDb] = useState(true);

    const defaultInterval = autorefreshIntervals.find((i) => i.is_active)?.interval ?? 0;
    const autoRefresh = useAutoRefresh("package-info-autoreload-button", defaultInterval);

    const repo = current as RepositoryId;

    const { data: packageData } = useQuery<PackageStatus[]>({
        queryKey: packageBase && repo ? QueryKeys.package(packageBase, repo) : ["package-none"],
        queryFn: () => Client.fetchPackage(packageBase!, repo),
        enabled: !!packageBase && !!repo && open,
        refetchInterval: autoRefresh.refetchInterval,
    });

    const { data: dependencies } = useQuery<Dependencies>({
        queryKey: packageBase && repo ? QueryKeys.dependencies(packageBase, repo) : ["deps-none"],
        queryFn: () => Client.fetchDependencies(packageBase!, repo),
        enabled: !!packageBase && !!repo && open,
    });

    const { data: patches = [] } = useQuery<Patch[]>({
        queryKey: packageBase ? QueryKeys.patches(packageBase) : ["patches-none"],
        queryFn: () => Client.fetchPatches(packageBase!),
        enabled: !!packageBase && open,
    });

    const description: PackageStatus | undefined = packageData?.[0];
    const pkg = description?.package;
    const status = description?.status;

    const headerStyle = status ? StatusHeaderStyles[status.status] : {};

    // Flatten depends from all sub-packages
    const allDepends: string[] = pkg
        ? Object.values(pkg.packages).flatMap((p: PackageProperties) => {
            const pkgNames = Object.keys(pkg.packages);
            const deps = (p.depends ?? []).filter((d: string) => !pkgNames.includes(d));
            const makeDeps = (p.make_depends ?? []).filter((d: string) => !pkgNames.includes(d)).map((d: string) => `${d} (make)`);
            const optDeps = (p.opt_depends ?? []).filter((d: string) => !pkgNames.includes(d)).map((d: string) => `${d} (optional)`);
            return [...deps, ...makeDeps, ...optDeps];
        })
        : [];

    const implicitDepends: string[] = dependencies
        ? Object.values(dependencies.paths).flat()
        : [];

    const groups: string[] = pkg
        ? Object.values(pkg.packages).flatMap((p: PackageProperties) => p.groups ?? [])
        : [];

    const licenses: string[] = pkg
        ? Object.values(pkg.packages).flatMap((p: PackageProperties) => p.licenses ?? [])
        : [];

    const upstreamUrls: string[] = pkg
        ? [...new Set(Object.values(pkg.packages).map((p: PackageProperties) => p.url).filter((u): u is string => !!u))].sort()
        : [];

    const aurUrl = pkg?.remote.web_url;

    const packagesList: string[] = pkg
        ? Object.entries(pkg.packages).map(([name, p]) => `${name}${p.description ? ` (${p.description})` : ""}`)
        : [];

    const handleUpdate = async (): Promise<void> => {
        if (!packageBase || !repo) {
            return;
        }
        try {
            await Client.addPackages(repo, { packages: [packageBase], refresh: refreshDb });
            showSuccess("Success", `Run update for packages ${packageBase}`);
        } catch (e) {
            const detail = e instanceof ApiError ? e.detail : String(e);
            showError("Action failed", `Package update failed: ${detail}`);
        }
    };

    const handleRemove = async (): Promise<void> => {
        if (!packageBase || !repo) {
            return;
        }
        try {
            await Client.removePackages(repo, [packageBase]);
            showSuccess("Success", `Packages ${packageBase} have been removed`);
            onClose();
        } catch (e) {
            const detail = e instanceof ApiError ? e.detail : String(e);
            showError("Action failed", `Could not remove package: ${detail}`);
        }
    };

    const handleDeletePatch = async (key: string): Promise<void> => {
        if (!packageBase) {
            return;
        }
        try {
            await Client.deletePatch(packageBase, key);
            void queryClient.invalidateQueries({ queryKey: QueryKeys.patches(packageBase) });
        } catch (e) {
            const detail = e instanceof ApiError ? e.detail : String(e);
            showError("Action failed", `Could not delete variable: ${detail}`);
        }
    };

    const handleReload = (): void => {
        if (!packageBase || !repo) {
            return;
        }
        void queryClient.invalidateQueries({ queryKey: QueryKeys.package(packageBase, repo) });
        void queryClient.invalidateQueries({ queryKey: QueryKeys.logs(packageBase, repo) });
        void queryClient.invalidateQueries({ queryKey: QueryKeys.changes(packageBase, repo) });
        void queryClient.invalidateQueries({ queryKey: QueryKeys.events(repo, packageBase) });
        void queryClient.invalidateQueries({ queryKey: QueryKeys.dependencies(packageBase, repo) });
        void queryClient.invalidateQueries({ queryKey: QueryKeys.patches(packageBase) });
    };

    const handleClose = (): void => {
        setTabIndex(0);
        setRefreshDb(true);
        onClose();
    };

    return (
        <Dialog open={open} onClose={handleClose} maxWidth="lg" fullWidth>
            <DialogTitle sx={headerStyle}>
                {pkg && status
                    ? `${pkg.base} ${status.status} at ${formatTimestamp(status.timestamp)}`
                    : packageBase ?? ""}
            </DialogTitle>
            <DialogContent>
                {pkg && (
                    <>
                        <Grid container spacing={1} sx={{ mt: 1 }}>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">packages</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{listToString(packagesList)}</Typography></Grid>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">version</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{pkg.version}</Typography></Grid>
                        </Grid>
                        <Grid container spacing={1} sx={{ mt: 0.5 }}>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">packager</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{pkg.packager ?? ""}</Typography></Grid>
                            <Grid item xs={4} md={1} />
                            <Grid item xs={8} md={5} />
                        </Grid>
                        <Grid container spacing={1} sx={{ mt: 0.5 }}>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">groups</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{listToString(groups)}</Typography></Grid>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">licenses</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{listToString(licenses)}</Typography></Grid>
                        </Grid>
                        <Grid container spacing={1} sx={{ mt: 0.5 }}>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">upstream</Typography></Grid>
                            <Grid item xs={8} md={5}>
                                {upstreamUrls.map((url) => (
                                    <Link key={url} href={url} target="_blank" rel="noopener" underline="hover" display="block" variant="body2">
                                        {url}
                                    </Link>
                                ))}
                            </Grid>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">AUR</Typography></Grid>
                            <Grid item xs={8} md={5}>
                                {aurUrl && (
                                    <Link href={aurUrl} target="_blank" rel="noopener" underline="hover" variant="body2">AUR link</Link>
                                )}
                            </Grid>
                        </Grid>
                        <Grid container spacing={1} sx={{ mt: 0.5 }}>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">depends</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{listToString(allDepends)}</Typography></Grid>
                            <Grid item xs={4} md={1}><Typography variant="body2" color="text.secondary" align="right">implicitly depends</Typography></Grid>
                            <Grid item xs={8} md={5}><Typography variant="body2">{listToString(implicitDepends)}</Typography></Grid>
                        </Grid>

                        {patches.length > 0 && (
                            <Box sx={{ mt: 2 }}>
                                <Typography variant="subtitle2" gutterBottom>Environment variables</Typography>
                                {patches.map((patch) => (
                                    <Box key={patch.key} sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}>
                                        <Chip label={patch.key} size="small" />
                                        <Typography variant="body2">=</Typography>
                                        <Typography variant="body2" sx={{ fontFamily: "monospace" }}>{JSON.stringify(patch.value)}</Typography>
                                        {hasAuth && (
                                            <IconButton size="small" color="error" onClick={() => void handleDeletePatch(patch.key)}>
                                                <DeleteIcon fontSize="small" />
                                            </IconButton>
                                        )}
                                    </Box>
                                ))}
                            </Box>
                        )}

                        <Box sx={{ borderBottom: 1, borderColor: "divider", mt: 2 }}>
                            <Tabs value={tabIndex} onChange={(_, v: number) => setTabIndex(v)}>
                                <Tab label="Build logs" />
                                <Tab label="Changes" />
                                <Tab label="Events" />
                            </Tabs>
                        </Box>

                        {tabIndex === 0 && packageBase && repo && (
                            <BuildLogsTab packageBase={packageBase} repo={repo} refetchInterval={autoRefresh.refetchInterval} />
                        )}
                        {tabIndex === 1 && packageBase && repo && (
                            <ChangesTab packageBase={packageBase} repo={repo} />
                        )}
                        {tabIndex === 2 && packageBase && repo && (
                            <EventsTab packageBase={packageBase} repo={repo} />
                        )}
                    </>
                )}
            </DialogContent>
            <DialogActions sx={{ flexWrap: "wrap", gap: 1 }}>
                {hasAuth && (
                    <>
                        <FormControlLabel
                            control={<Checkbox checked={refreshDb} onChange={(_, checked) => setRefreshDb(checked)} size="small" />}
                            label="update pacman databases"
                        />
                        <Button onClick={() => void handleUpdate()} variant="contained" color="success" startIcon={<PlayArrowIcon />} size="small">
                            update
                        </Button>
                        <Button onClick={() => void handleRemove()} variant="contained" color="error" startIcon={<DeleteIcon />} size="small">
                            remove
                        </Button>
                    </>
                )}
                <Button onClick={handleReload} variant="outlined" color="secondary" startIcon={<RefreshIcon />} size="small">
                    reload
                </Button>
                <AutoRefreshControl
                    intervals={autorefreshIntervals}
                    enabled={autoRefresh.enabled}
                    currentInterval={autoRefresh.interval}
                    onToggle={autoRefresh.setEnabled}
                    onIntervalChange={autoRefresh.setInterval}
                />
                <Button onClick={handleClose} variant="contained" startIcon={<CloseIcon />} size="small">
                    close
                </Button>
            </DialogActions>
        </Dialog>
    );
}
