/*
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * Copyright (C) 2016 Red Hat, Inc.
 */
import React, { useState } from 'react';
import { useInit } from 'hooks';
import cockpit from 'cockpit';

import type { optString, VM, VMDisk } from '../../../types';
import type { DeleteResourceModalProps } from '../../common/deleteResource';

import { Button } from "@patternfly/react-core/dist/esm/components/Button";
import { DescriptionListDescription, DescriptionListGroup, DescriptionListTerm } from "@patternfly/react-core/dist/esm/components/DescriptionList";
import { DropdownItem } from "@patternfly/react-core/dist/esm/components/Dropdown";
import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip";

import { KebabDropdown } from 'cockpit-components-dropdown.jsx';
import { basename } from 'cockpit-path';

import { useDialogs } from 'dialogs.jsx';
import {
    StorageDeleteInfo, getStorageDeleteInfoForDisk, deleteStorage,
    virtXmlHotRemove,
    domainGet
} from '../../../libvirtApi/domain.js';
import { MediaEjectModal } from './mediaEject.jsx';
import { EditDiskAction } from './diskEdit.jsx';
import { AddDisk } from './diskAdd.jsx';
import { DeleteResourceModal } from '../../common/deleteResource.jsx';
import { appState } from '../../../state';

const _ = cockpit.gettext;

type DiskSource = VMDisk["source"];
type DiskSourceKey = keyof DiskSource | "port";

export const DISK_SOURCE_LIST: { name: DiskSourceKey, label: string }[] = [
    { name: "file", label: _("File") },
    { name: "dev", label: _("Block device") },
    { name: "protocol", label: _("Protocol") },
    { name: "pool", label: _("Pool") },
    { name: "volume", label: _("Volume") },
    { name: "name", label: _("Host") },
    { name: "port", label: _("Port") },
];

export function getDiskSourceValue(diskSource: DiskSource, value: DiskSourceKey): optString {
    if (value === "host")
        return diskSource.host.name;
    else if (value === "port")
        return diskSource.host.port;
    else
        return diskSource[value];
}

export const DiskSourceDescriptions = ({
    diskSource,
    idPrefix
} : {
    diskSource: DiskSource,
    idPrefix: string,
}) => {
    const addOptional = (chunks: React.ReactNode[], value: optString, type: string, descr: string) => {
        if (value) {
            chunks.push(
                <DescriptionListGroup key={descr}>
                    <DescriptionListTerm>
                        {descr}
                    </DescriptionListTerm>
                    <DescriptionListDescription id={`${idPrefix}-source-${type}`}>
                        {value}
                    </DescriptionListDescription>
                </DescriptionListGroup>
            );
        }
    };

    const chunks: React.ReactNode[] = [];
    DISK_SOURCE_LIST.forEach(entry => addOptional(chunks, getDiskSourceValue(diskSource, entry.name), entry.name, entry.label));

    return chunks;
};

export const DiskSourceAbbrev = ({
    disk,
} : {
    disk: VMDisk,
}) => {
    const diskSource = disk.source;
    if (diskSource.file) {
        return basename(diskSource.file);
    } else if (diskSource.dev) {
        return diskSource.dev;
    } else if (diskSource.volume) {
        return diskSource.volume;
    } else if (diskSource.host.name && diskSource.host.port) {
        return diskSource.host.name + ":" + diskSource.host.port;
    } else if (diskSource.host.name) {
        return diskSource.host.name;
    } else if (disk.device == "cdrom") {
        return <div className="pf-v6-u-text-color-subtle">{_("(empty)")}</div>;
    } else {
        return null;
    }
};

export const DiskExtraDescriptions = ({
    idPrefix,
    type,
    serial
} : {
    idPrefix: string,
    type: optString,
    serial: optString,
}) => {
    const addOptional = (chunks: React.ReactNode[], value: optString, type: string, descr: string) => {
        if (value) {
            chunks.push(
                <DescriptionListGroup key={descr}>
                    <DescriptionListTerm>
                        {descr}
                    </DescriptionListTerm>
                    <DescriptionListDescription id={`${idPrefix}-${type}`}>
                        {value}
                    </DescriptionListDescription>
                </DescriptionListGroup>
            );
        }
    };

    const chunks: React.ReactNode[] = [];
    addOptional(chunks, serial, "serial", _("Serial"));
    addOptional(chunks, type, "type", _("Format"));

    return chunks;
};

export const RemoveDiskModal = ({
    vm,
    disk,
} : {
    vm: VM,
    disk: VMDisk,
}) => {
    const [info, setInfo] = useState<null | StorageDeleteInfo>(null);
    useInit(async () => { setInfo(await getStorageDeleteInfoForDisk(vm, disk)) });

    const onRemoveDisk = async (deleteInfo: null | StorageDeleteInfo) => {
        await virtXmlHotRemove(vm, "disk", { target: disk.target }, disk.target in vm.inactiveXML.disks);
        await domainGet(vm);

        if (deleteInfo) {
            try {
                await deleteStorage(vm, [deleteInfo]);
            } catch (exc) {
                appState.addNotification({
                    resourceId: vm.id,
                    text: cockpit.format(_("Could not delete disk's storage")),
                    detail: String(exc),
                    type: "warning"
                });
            }
        }
    };

    const dialogProps: DeleteResourceModalProps = {
        title: _("Remove disk from VM?"),
        actionName: _("Remove"),
        errorMessage: cockpit.format(_("Disk $0 could not be removed"), disk.target),
        actionDescription: cockpit.format(_("This disk will be removed from $0:"), vm.name),
        objectDescription: [
            { name: _("Target"), value: <span className="ct-monospace">{disk.target}</span> },
            ...DISK_SOURCE_LIST.flatMap(entry => getDiskSourceValue(disk.source, entry.name)
                ? { name: entry.label, value: <span className="ct-monospace">{getDiskSourceValue(disk.source, entry.name)}</span> }
                : [])
        ],
        deleteHandler: () => onRemoveDisk(null),
    };

    if (info) {
        dialogProps.actionNameSecondary = _("Remove disk and delete disk image");
        dialogProps.deleteHandlerSecondary = () => onRemoveDisk(info);
    }

    return <DeleteResourceModal {...dialogProps} />;
};

export const DiskActions = ({
    vm,
    disk,
    supportedDiskBusTypes,
    idPrefixRow,
    isActionOpen,
    setIsActionOpen
} : {
    vm: VM,
    disk: VMDisk,
    supportedDiskBusTypes: string[],
    idPrefixRow: string,
    isActionOpen: boolean,
    setIsActionOpen: (open: boolean) => void,
}) => {
    const Dialogs = useDialogs();

    function openMediaInsertionDialog() {
        Dialogs.show(
            <AddDisk
                idPrefix={idPrefixRow + "-insert-dialog-adddisk"}
                vm={vm}
                isMediaInsertion disk={disk}
            />
        );
    }

    let cdromAction;
    if (disk.device === "cdrom" && ["file", "volume"].includes(disk.type || "")) {
        if (!disk.source.file && !(disk.source.pool && disk.source.volume)) {
            cdromAction = (
                <Button id={`${idPrefixRow}-insert`}
                              variant='secondary'
                              onClick={openMediaInsertionDialog}
                              isDisabled={supportedDiskBusTypes.length == 0}>
                    {_("Insert")}
                </Button>
            );
        } else {
            cdromAction = (
                <Button id={`${idPrefixRow}-eject-button`}
                    variant="secondary"
                    onClick={() => Dialogs.show(<MediaEjectModal idPrefix={idPrefixRow} vm={vm} disk={disk} />)}>
                    {_("Eject")}
                </Button>
            );
        }
    }

    const disabled = vm.state != 'shut off' && vm.state != 'running';

    let button = (
        <DropdownItem className="pf-m-danger"
                      id={`delete-${idPrefixRow}`}
                      key={`delete-${idPrefixRow}`}
                      isDisabled={disabled}
                      onClick={() => {
                          Dialogs.show(<RemoveDiskModal vm={vm} disk={disk} />);
                      }}>
            {_("Remove")}
        </DropdownItem>
    );

    if (disabled) {
        button = (
            <Tooltip id={`delete-${idPrefixRow}-tooltip`}
                     key={`delete-${idPrefixRow}-tooltip`}
                     content={_("The VM needs to be running or shut off to detach this device")}>
                <span>{button}</span>
            </Tooltip>
        );
    }

    const dropdownItems = [
        button,
    ];

    return (
        <div className='machines-listing-actions'>
            { cdromAction }
            { vm.persistent && vm.inactiveXML.disks[disk.target] && // supported only  for persistent disks
                <EditDiskAction
                    disk={vm.inactiveXML.disks[disk.target]}
                    vm={vm}
                    idPrefix={`${idPrefixRow}-edit`}
                />
            }
            <KebabDropdown
                key={idPrefixRow + "-action-kebab"}
                toggleButtonId={idPrefixRow + "-action-kebab"}
                position='right'
                dropdownItems={dropdownItems}
                isOpen={isActionOpen}
                setIsOpen={setIsActionOpen as React.Dispatch<React.SetStateAction<boolean>>}
            />
        </div>
    );
};
