/*
 * Copyright (c) 2021-2026 ahriman team.
 *
 * This file is part of ahriman
 * (see https://github.com/arcan1s/ahriman).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import AddIcon from "@mui/icons-material/Add";
import DeleteIcon from "@mui/icons-material/Delete";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import {
    Autocomplete,
    Box,
    Button,
    Checkbox,
    Dialog,
    DialogActions,
    DialogContent,
    FormControlLabel,
    IconButton,
    TextField,
} from "@mui/material";
import { useQuery } from "@tanstack/react-query";
import { ApiError } from "api/client/ApiError";
import DialogHeader from "components/common/DialogHeader";
import RepositorySelect from "components/common/RepositorySelect";
import { QueryKeys } from "hooks/QueryKeys";
import { useClient } from "hooks/useClient";
import { useDebounce } from "hooks/useDebounce";
import { useNotification } from "hooks/useNotification";
import { useSelectedRepository } from "hooks/useSelectedRepository";
import type { AURPackage } from "models/AURPackage";
import type { PackageActionRequest } from "models/PackageActionRequest";
import React, { useRef, useState } from "react";

interface EnvironmentVariable {
    id: number;
    key: string;
    value: string;
}

interface PackageAddDialogProps {
    onClose: () => void;
    open: boolean;
}

export default function PackageAddDialog({ onClose, open }: PackageAddDialogProps): React.JSX.Element {
    const client = useClient();
    const { showSuccess, showError } = useNotification();
    const repositorySelect = useSelectedRepository();

    const [packageName, setPackageName] = useState("");
    const [refreshDatabase, setRefreshDatabase] = useState(true);
    const [environmentVariables, setEnvironmentVariables] = useState<EnvironmentVariable[]>([]);
    const variableIdCounter = useRef(0);

    const handleClose = (): void => {
        setPackageName("");
        repositorySelect.reset();
        setRefreshDatabase(true);
        setEnvironmentVariables([]);
        onClose();
    };

    const debouncedSearch = useDebounce(packageName, 500);

    const { data: searchResults = [] } = useQuery<AURPackage[]>({
        enabled: debouncedSearch.length >= 3,
        queryFn: () => client.service.servicePackageSearch(debouncedSearch),
        queryKey: QueryKeys.search(debouncedSearch),
    });

    const handleSubmit = async (action: "add" | "request"): Promise<void> => {
        if (!packageName) {
            return;
        }
        const repository = repositorySelect.selectedRepository;
        if (!repository) {
            return;
        }
        try {
            const patches = environmentVariables.filter(variable => variable.key);
            const request: PackageActionRequest = { packages: [packageName], patches };
            if (action === "add") {
                request.refresh = refreshDatabase;
                await client.service.servicePackageAdd(repository, request);
            } else {
                await client.service.servicePackageRequest(repository, request);
            }
            handleClose();
            showSuccess("Success", `Packages ${packageName} have been ${action === "add" ? "added" : "requested"}`);
        } catch (exception) {
            const detail = ApiError.errorDetail(exception);
            showError("Action failed", `Package ${action} failed: ${detail}`);
        }
    };

    return <Dialog fullWidth maxWidth="md" onClose={handleClose} open={open}>
        <DialogHeader onClose={handleClose}>
            Add new packages
        </DialogHeader>

        <DialogContent>
            <RepositorySelect repositorySelect={repositorySelect} />

            <Autocomplete
                freeSolo
                inputValue={packageName}
                onInputChange={(_, value) => setPackageName(value)}
                options={searchResults.map(pkg => pkg.package)}
                renderInput={params =>
                    <TextField {...params} label="package" margin="normal" placeholder="AUR package" />
                }
                renderOption={(props, option) => {
                    const pkg = searchResults.find(pkg => pkg.package === option);
                    return <li {...props} key={option}>
                        {option}{pkg ? ` (${pkg.description})` : ""}
                    </li>;
                }}
            />

            <FormControlLabel
                control={<Checkbox checked={refreshDatabase} onChange={(_, checked) => setRefreshDatabase(checked)} />}
                label="update pacman databases"
            />

            <Button
                fullWidth
                onClick={() => {
                    const id = variableIdCounter.current++;
                    setEnvironmentVariables(prev => [...prev, { id, key: "", value: "" }]);
                }}
                startIcon={<AddIcon />}
                sx={{ mt: 1 }}
                variant="outlined"
            >
                add environment variable
            </Button>

            {environmentVariables.map(variable =>
                <Box key={variable.id} sx={{ alignItems: "center", display: "flex", gap: 1, mt: 1 }}>
                    <TextField
                        onChange={event => {
                            const newKey = event.target.value;
                            setEnvironmentVariables(prev =>
                                prev.map(entry => entry.id === variable.id ? { ...entry, key: newKey } : entry),
                            );
                        }}
                        placeholder="name"
                        size="small"
                        sx={{ flex: 1 }}
                        value={variable.key}
                    />
                    <Box>=</Box>
                    <TextField
                        placeholder="value"
                        onChange={event => {
                            const newValue = event.target.value;
                            setEnvironmentVariables(prev =>
                                prev.map(entry => entry.id === variable.id ? { ...entry, value: newValue } : entry),
                            );
                        }}
                        size="small"
                        sx={{ flex: 1 }}
                        value={variable.value}
                    />
                    <IconButton
                        aria-label="Remove variable"
                        color="error"
                        onClick={() => setEnvironmentVariables(prev => prev.filter(entry => entry.id !== variable.id))}
                        size="small"
                    >
                        <DeleteIcon />
                    </IconButton>
                </Box>,
            )}
        </DialogContent>

        <DialogActions>
            <Button onClick={() => void handleSubmit("add")} startIcon={<PlayArrowIcon />} variant="contained">add</Button>
            <Button color="success" onClick={() => void handleSubmit("request")} startIcon={<AddIcon />} variant="contained">request</Button>
        </DialogActions>
    </Dialog>;
}
