<script setup lang="ts">
/**
 * ServiceCredentialsGroupsList Component
 *
 * A component that displays a list of service credential groups using cards.
 * Provides functionality to view, edit, and delete credential groups for tools.
 *
 * Features:
 * - Card-based display of credential groups
 * - Edit credentials with modal form
 * - Delete credentials with confirmation
 * - Visual indicators for groups in use
 * - Tool association badges
 * - Real-time status updates
 *
 * @component ServiceCredentialsGroupsList
 * @example
 * <ServiceCredentialsGroupsList :service-groups="groups" />
 */

import { faExclamationTriangle, faKey, faPencilAlt, faTrash, faWrench } from "@fortawesome/free-solid-svg-icons";
import { BModal } from "bootstrap-vue";
import { faCheck } from "font-awesome-6";
import { storeToRefs } from "pinia";
import { computed, ref } from "vue";

import type { ServiceCredentialGroupPayload, ServiceCredentialsDefinition } from "@/api/userCredentials";
import type { CardAction, CardBadge, TitleIcon } from "@/components/Common/GCard.types";
import { useConfirmDialog } from "@/composables/confirmDialog";
import { Toast } from "@/composables/toast";
import { useUserToolCredentials } from "@/composables/userToolCredentials";
import { useToolStore } from "@/stores/toolStore";
import {
    SECRET_PLACEHOLDER,
    type ServiceCredentialsGroupDetails,
    useUserToolsServiceCredentialsStore,
} from "@/stores/userToolsServiceCredentialsStore";
import { errorMessageAsString } from "@/utils/simple-error";

import GCard from "@/components/Common/GCard.vue";
import CredentialsGroupForm from "@/components/User/Credentials/CredentialsGroupForm.vue";

/**
 * Edit data structure for credential group editing
 * @interface EditData
 */
interface EditData {
    /** Source tool ID. */
    sourceId: string;
    /** Source tool version. */
    sourceVersion: string;
    /** Group data including ID, payload, and new group flag. */
    groupData: {
        groupId: string;
        groupPayload: ServiceCredentialGroupPayload;
        isNewGroup: boolean;
    };
    /** Service definition for the credentials. */
    serviceDefinition: ServiceCredentialsDefinition;
}

interface Props {
    /**
     * Array of service credential groups to display
     * @type {ServiceCredentialsGroupDetails[]}
     */
    serviceGroups: ServiceCredentialsGroupDetails[];
}

const props = defineProps<Props>();

const { confirm } = useConfirmDialog();

const { getToolForId, getToolNameById } = useToolStore();

const userToolsServiceCredentialsStore = useUserToolsServiceCredentialsStore();
const { userToolsServicesCurrentGroupIds } = storeToRefs(userToolsServiceCredentialsStore);

/** Controls modal visibility. */
const showModal = ref(false);
/** Text for the save button. */
const saveButtonText = ref("Save Changes");
/** Data for the currently edited group. */
const editData = ref<EditData>();

/** Icon configuration for card titles. */
const cardTitleIcon: TitleIcon = { icon: faKey, size: "sm" };

/**
 * Generates the display title for a credential group card.
 * @param {ServiceCredentialsGroupDetails} group - The credential group.
 * @returns {string} Formatted title string.
 */
const cardTitle = computed(() => (group: ServiceCredentialsGroupDetails) => {
    return `${group.serviceDefinition.name} (v${group.serviceDefinition.version}) - ${group.name}`;
});

/**
 * Checks if the source tool for a credential group is missing/deleted.
 * @param {ServiceCredentialsGroupDetails} group - The credential group to check.
 * @returns {boolean} True if the tool is no longer available.
 */
const isToolMissing = computed(() => (group: ServiceCredentialsGroupDetails) => {
    return !getToolForId(group.sourceId);
});

/**
 * Checks if a credential group is currently in use by any tool.
 * @param {ServiceCredentialsGroupDetails} group - The credential group to check.
 * @returns {boolean} True if the group is in use.
 */
const isGroupInUse = computed(() => (group: ServiceCredentialsGroupDetails) => {
    if (isToolMissing.value(group)) {
        return false;
    }

    const userToolKey = userToolsServiceCredentialsStore.getUserToolKey(group.sourceId, group.sourceVersion);
    const userToolService = userToolsServicesCurrentGroupIds.value[userToolKey];
    for (const groupId of Object.values(userToolService || {})) {
        if (groupId === group.id) {
            return true;
        }
    }
    return false;
});

/**
 * Gets the display name for a tool, with a fallback for missing/deleted tools.
 * @param {ServiceCredentialsGroupDetails} group - The credential group.
 * @returns {string} The tool name or a fallback indicator.
 */
const getToolDisplayName = computed(() => (group: ServiceCredentialsGroupDetails) => {
    if (isToolMissing.value(group)) {
        return `${group.sourceId} (deleted)`;
    }
    return getToolNameById(group.sourceId);
});

/**
 * Deletes a credential group after user confirmation.
 * @param {ServiceCredentialsGroupDetails} groupToDelete - The group to delete.
 * @returns {Promise<void>} Resolves when deletion is complete.
 * @throws {Error} When deletion fails.
 */
async function deleteGroup(groupToDelete: ServiceCredentialsGroupDetails): Promise<void> {
    let message = `Are you sure you want to delete the credentials group "${groupToDelete.name}"?`;

    if (isToolMissing.value(groupToDelete)) {
        message = message.concat(` The associated tool is no longer available.`);
    } else if (isGroupInUse.value(groupToDelete)) {
        message = message.concat(` This group is currently in use by '${getToolNameById(groupToDelete.sourceId)}'.`);
    }

    const confirmed = await confirm(message, {
        title: "Delete credentials group",
        okTitle: "Delete group",
        okVariant: "danger",
        cancelVariant: "outline-primary",
        centered: true,
    });

    if (confirmed && groupToDelete) {
        const { deleteCredentialsGroup } = useUserToolCredentials(groupToDelete.sourceId, groupToDelete.sourceVersion);

        try {
            await deleteCredentialsGroup(groupToDelete.serviceDefinition, groupToDelete.id);
            Toast.success("Credentials group deleted successfully");
        } catch (error) {
            console.error("Error deleting group:", error);
            Toast.error(`Error deleting group: ${errorMessageAsString(error)}`);
        }
    }
}

/**
 * Prepares a credential group for editing in the modal.
 * @param {ServiceCredentialsGroupDetails} group - The group to edit.
 * @returns {void}
 */
function editGroup(group: ServiceCredentialsGroupDetails): void {
    editData.value = {
        sourceId: group.sourceId,
        sourceVersion: group.sourceVersion,
        groupData: {
            groupId: group.id,
            groupPayload: {
                name: group.name,
                secrets: group.secrets.map((secret) => ({
                    name: secret.name,
                    value: secret.is_set ? SECRET_PLACEHOLDER : "",
                })),
                variables: group.variables.map((variable) => ({
                    name: variable.name,
                    value: variable.value,
                })),
            },
            isNewGroup: false,
        },
        serviceDefinition: group.serviceDefinition,
    };

    showModal.value = true;
}

/**
 * Saves changes to the currently edited credential group.
 * @returns {Promise<void>} Resolves when save is complete.
 * @throws {Error} When saving fails.
 */
async function onSaveChanges(): Promise<void> {
    if (!editData.value) {
        return;
    }

    const { busyMessage } = storeToRefs(userToolsServiceCredentialsStore);
    const { saveUserCredentials } = useUserToolCredentials(editData.value.sourceId, editData.value.sourceVersion);

    try {
        const groupToUpdate = editData.value;

        if (!groupToUpdate) {
            console.warn(`No group found for update`);
            return;
        }

        saveButtonText.value = busyMessage.value;

        await saveUserCredentials(
            groupToUpdate.serviceDefinition,
            groupToUpdate.groupData.groupId,
            groupToUpdate.groupData.groupPayload,
        );

        Toast.success("Credentials group updated successfully");

        editData.value = undefined;
    } catch (error) {
        console.error("Error saving credentials:", error);
        Toast.error(`Error saving credentials: ${errorMessageAsString(error)}`);
    } finally {
        saveButtonText.value = "Save";
    }
}

/**
 * Generates badge configuration for a credential group card.
 * @param {ServiceCredentialsGroupDetails} group - The credential group.
 * @returns {CardBadge[]} Array of badge configurations.
 */
function getBadgesFor(group: ServiceCredentialsGroupDetails): CardBadge[] {
    const toolMissing = isToolMissing.value(group);
    const badges: CardBadge[] = [];

    if (toolMissing) {
        badges.push({
            id: `tool-missing-${group.id}`,
            icon: faExclamationTriangle,
            title: "The tool associated with these credentials is no longer available. You cannot edit or use this group.",
            label: "Tool Unavailable",
            variant: "warning",
        });
    }

    badges.push({
        id: `tool-${group.sourceId}`,
        icon: faWrench,
        title: toolMissing
            ? "This tool is no longer available."
            : "This tool is using this credentials group. Click to view.",
        label: getToolDisplayName.value(group),
        to: toolMissing ? undefined : `/root?tool_id=${group.sourceId}&tool_version=${group.sourceVersion}`,
    });

    if (!toolMissing) {
        badges.push({
            id: `in-use-${group.id}`,
            icon: faCheck,
            title: "This group is currently in use.",
            label: "In Use",
            variant: "success",
            visible: isGroupInUse.value(group),
        });
    }

    return badges;
}

/**
 * Generates primary action configuration for a credential group card
 * @param {ServiceCredentialsGroupDetails} group - The credential group
 * @returns {CardAction[]} Array of action configurations
 */
function getPrimaryActions(group: ServiceCredentialsGroupDetails): CardAction[] {
    const toolMissing = isToolMissing.value(group);
    const primaryActions: CardAction[] = [
        {
            id: `delete-${group.id}`,
            label: "Delete",
            title: "Delete this group",
            icon: faTrash,
            variant: "outline-danger",
            handler: () => deleteGroup(group),
        },
        {
            id: `edit-${group.id}`,
            label: "Edit",
            title: !toolMissing ? "Cannot edit - tool definition not available" : "Edit this group",
            icon: faPencilAlt,
            variant: "outline-info",
            handler: () => editGroup(group),
            disabled: toolMissing,
        },
    ];
    return primaryActions;
}
</script>

<template>
    <div>
        <GCard
            v-for="group in props.serviceGroups"
            :id="`group-${group.id}-${group.name}`"
            :key="`group-${group.id}-${group.name}`"
            :title="cardTitle(group)"
            :title-icon="cardTitleIcon"
            :description="group.serviceDefinition.description"
            :badges="getBadgesFor(group)"
            :published="isGroupInUse(group)"
            :primary-actions="getPrimaryActions(group)"
            :update-time="group.update_time">
        </GCard>

        <BModal
            v-model="showModal"
            visible
            centered
            scrollable
            no-close-on-backdrop
            no-close-on-esc
            button-size="md"
            size="lg"
            body-class="edit-credentials-body"
            :title="`Edit Credentials Group - ${editData?.groupData.groupPayload.name}`"
            :ok-title="saveButtonText"
            cancel-title="Close"
            cancel-variant="outline-danger"
            @ok="onSaveChanges">
            <CredentialsGroupForm
                v-if="editData"
                :group-data="editData.groupData"
                :service-definition="editData.serviceDefinition" />
        </BModal>
    </div>
</template>
