<script setup lang="ts">
import FormSection from '@/Components/FormSection.vue';
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
import { computed, ref, inject, type ComputedRef } from 'vue';
import { Field, FieldLabel, FieldDescription, FieldError } from '@/packages/ui/src/field';
import { api, type ApiToken, type CreateApiTokenBody } from '@/packages/api/src';
import SectionBorder from '@/Components/SectionBorder.vue';
import DangerButton from '@/packages/ui/src/Buttons/DangerButton.vue';
import TextInput from '../../../packages/ui/src/Input/TextInput.vue';
import SecondaryButton from '../../../packages/ui/src/Buttons/SecondaryButton.vue';
import DialogModal from '@/packages/ui/src/DialogModal.vue';
import ActionMessage from '@/Components/ActionMessage.vue';
import ConfirmationModal from '@/Components/ConfirmationModal.vue';
import ActionSection from '@/Components/ActionSection.vue';
import { useForm } from '@inertiajs/vue3';
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query';
import { useNotificationsStore } from '@/utils/notification';
import { useClipboard } from '@vueuse/core';
import { formatDateTimeLocalized } from '../../../packages/ui/src/utils/time';
import { ClockIcon } from '@heroicons/vue/20/solid';
import type { Organization } from '@/packages/api/src';

const queryClient = useQueryClient();

const apiTokenBeingDeleted = ref<ApiToken | null>(null);
const apiTokenBeingRevoked = ref<ApiToken | null>(null);

const { handleApiRequestNotifications } = useNotificationsStore();
const newToken = ref('');

const { copy, copied, isSupported } = useClipboard();

const organization = inject<ComputedRef<Organization>>('organization');

async function createApiToken() {
    await handleApiRequestNotifications(
        () =>
            createApiTokenMutation.mutateAsync({
                name: createApiTokenForm.name,
            }),
        'API Token successfully created',
        'There was an error while creating the API Token',
        (response) => {
            createApiTokenForm.name = '';
            displayingToken.value = true;
            newToken.value = response.data.access_token;
        }
    );
}

const createApiTokenForm = useForm({
    name: '',
});

function confirmApiTokenDeletion(token: ApiToken) {
    apiTokenBeingDeleted.value = token;
}

function confirmApiTokenRevocation(token: ApiToken) {
    apiTokenBeingRevoked.value = token;
}

const displayingToken = ref(false);

async function deleteApiToken() {
    if (apiTokenBeingDeleted.value) {
        await handleApiRequestNotifications(
            () => deleteApiTokenMutation.mutateAsync(apiTokenBeingDeleted.value!.id),
            'API Token successfully deleted',
            'There was an error while deleting the API Token',
            () => {
                apiTokenBeingDeleted.value = null;
            }
        );
    }
}

async function revokeApiToken() {
    if (apiTokenBeingRevoked.value) {
        await handleApiRequestNotifications(
            () => revokeApiTokenMutation.mutateAsync(apiTokenBeingRevoked.value!.id),
            'API Token successfully revoked',
            'There was an error while revoking the API Token',
            () => {
                apiTokenBeingRevoked.value = null;
            }
        );
    }
}

const { data: sharedReportResponseData } = useQuery({
    queryKey: ['api-tokens'],
    queryFn: () => api.getApiTokens(),
});

const tokens = computed(() => {
    return sharedReportResponseData.value?.data ?? [];
});

const createApiTokenMutation = useMutation({
    mutationFn: async (apiToken: CreateApiTokenBody) => {
        return await api.createApiToken(apiToken);
    },
    onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['api-tokens'] });
    },
});

const deleteApiTokenMutation = useMutation({
    mutationFn: async (apiTokenId: string) => {
        return await api.deleteApiToken(undefined, {
            params: {
                apiToken: apiTokenId,
            },
        });
    },
    onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['api-tokens'] });
    },
});

const revokeApiTokenMutation = useMutation({
    mutationFn: async (apiTokenId: string) => {
        return await api.revokeApiToken(undefined, {
            params: {
                apiToken: apiTokenId,
            },
        });
    },
    onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['api-tokens'] });
    },
});
</script>

<template>
    <div>
        <!-- Generate API Token -->
        <FormSection @submitted="createApiToken">
            <template #title> Create API Token </template>

            <template #description>
                API tokens allow third-party services to authenticate with our application on your
                behalf.
            </template>

            <template #form>
                <!-- Token Name -->
                <Field class="col-span-6 sm:col-span-4">
                    <FieldLabel for="api_key_name">API Key Name</FieldLabel>
                    <TextInput
                        id="api_key_name"
                        v-model="createApiTokenForm.name"
                        type="text"
                        class="block w-full" />
                    <FieldError v-if="createApiTokenForm.errors.name">{{
                        createApiTokenForm.errors.name
                    }}</FieldError>
                    <FieldDescription>
                        <span class="flex space-x-1.5 items-center text-text-tertiary font-medium">
                            <ClockIcon class="w-4"></ClockIcon>
                            <span> API Tokens are valid for 1 year </span>
                        </span>
                    </FieldDescription>
                </Field>
            </template>

            <template #actions>
                <ActionMessage :on="createApiTokenForm.recentlySuccessful" class="me-3">
                    Created.
                </ActionMessage>

                <PrimaryButton
                    :class="{ 'opacity-25': createApiTokenForm.processing }"
                    :disabled="createApiTokenForm.processing">
                    Create API Key
                </PrimaryButton>
            </template>
        </FormSection>

        <div v-if="tokens.length > 0">
            <SectionBorder />

            <!-- Manage API Tokens -->
            <div class="mt-10 sm:mt-0">
                <ActionSection>
                    <template #title> Manage API Tokens </template>

                    <template #description>
                        You may delete or revoke any of your existing tokens if they are no longer
                        needed.
                    </template>

                    <!-- API Token List -->
                    <template #content>
                        <div class="divide-border-secondary divide-y">
                            <div
                                v-for="token in tokens"
                                :key="token.id"
                                class="flex items-center py-2.5 justify-between">
                                <div class="break-all text-text-primary">
                                    <div>{{ token.name }}</div>
                                    <div class="text-sm text-text-tertiary space-x-3">
                                        <span v-if="token.created_at">
                                            Created at
                                            {{
                                                formatDateTimeLocalized(
                                                    token.created_at,
                                                    organization?.date_format,
                                                    organization?.time_format
                                                )
                                            }}
                                        </span>
                                        <span v-if="token.expires_at">
                                            Expires at
                                            {{
                                                formatDateTimeLocalized(
                                                    token.expires_at,
                                                    organization?.date_format,
                                                    organization?.time_format
                                                )
                                            }}
                                        </span>
                                        <span v-if="token.revoked"> Revoked </span>
                                    </div>
                                </div>

                                <div class="flex items-center ms-2">
                                    <div v-if="token.last_used_ago" class="text-sm text-gray-400">
                                        Last used {{ token.last_used_ago }}
                                    </div>
                                    <button
                                        v-if="!token.revoked"
                                        class="cursor-pointer ms-6 text-sm text-text-secondary"
                                        :aria-label="'Revoke API Token ' + token.name"
                                        @click="confirmApiTokenRevocation(token)">
                                        Revoke
                                    </button>
                                    <button
                                        class="cursor-pointer ms-6 text-sm text-red-500"
                                        :aria-label="'Delete API Token ' + token.name"
                                        @click="confirmApiTokenDeletion(token)">
                                        Delete
                                    </button>
                                </div>
                            </div>
                        </div>
                    </template>
                </ActionSection>
            </div>
        </div>

        <!-- Token Value Modal -->
        <DialogModal :show="displayingToken" @close="displayingToken = false">
            <template #title> API Token created successfully </template>

            <template #content>
                <div>
                    Please copy your new API token. For your security, it won't be shown again.
                    <strong>This token is valid for one year</strong> unless you revoke it.
                </div>

                <div></div>

                <div class="flex gap-2 pt-6 w-full">
                    <TextInput
                        v-if="newToken"
                        disabled
                        :model-value="newToken"
                        class="flex-1 text-gray-500"></TextInput>
                    <PrimaryButton v-if="isSupported" @click="copy(newToken)">{{
                        copied ? 'Copied!' : 'Copy Token'
                    }}</PrimaryButton>
                </div>
            </template>

            <template #footer>
                <SecondaryButton @click="displayingToken = false"> Close </SecondaryButton>
            </template>
        </DialogModal>

        <!-- Delete Token Confirmation Modal -->
        <ConfirmationModal
            :show="apiTokenBeingDeleted != null"
            @close="apiTokenBeingDeleted = null">
            <template #title> Delete API Token </template>

            <template #content> Are you sure you would like to delete this API token? </template>

            <template #footer>
                <SecondaryButton @click="apiTokenBeingDeleted = null"> Cancel </SecondaryButton>

                <DangerButton
                    class="ms-3"
                    :class="{ 'opacity-25': createApiTokenMutation.isPending.value }"
                    :disabled="createApiTokenMutation.isPending.value"
                    @click="deleteApiToken">
                    Delete
                </DangerButton>
            </template>
        </ConfirmationModal>

        <ConfirmationModal
            :show="apiTokenBeingRevoked != null"
            @close="apiTokenBeingRevoked = null">
            <template #title> Revoke API Token </template>

            <template #content> Are you sure you would like to revoke this API token? </template>

            <template #footer>
                <SecondaryButton @click="apiTokenBeingRevoked = null"> Cancel </SecondaryButton>

                <DangerButton
                    class="ms-3"
                    :class="{ 'opacity-25': revokeApiTokenMutation.isPending.value }"
                    :disabled="revokeApiTokenMutation.isPending.value"
                    @click="revokeApiToken">
                    Revoke
                </DangerButton>
            </template>
        </ConfirmationModal>
    </div>
</template>
