<script setup lang="ts">
import { onMounted, reactive, watch, computed } from "vue";
import BootstrapModal from "@/components/BootstrapModal.vue";
import { Modal } from "bootstrap";
import dayjs from "dayjs";
import type {
  BucketPermission,
  S3ObjectMetaInformation,
  BucketPermissionParameters,
} from "@/client";
import type { ComputedRef } from "vue";
import { PermissionEnum, BucketPermissionsService } from "@/client";
import { Toast } from "bootstrap";
import BootstrapIcon from "@/components/BootstrapIcon.vue";

// Types
// -----------------------------------------------------------------------------
interface S3ObjectWithFolder extends S3ObjectMetaInformation {
  folder: string[];
  pseudoFileName: string;
}

type FolderTree = {
  subFolders: Record<string, FolderTree>;
  files: S3ObjectWithFolder[];
};

// Props
// -----------------------------------------------------------------------------
const props = defineProps<{
  modalID: string;
  modalLabel: string;
  bucketName: string;
  subFolders: FolderTree;
  editUserPermission: BucketPermission | undefined;
  readonly: boolean;
}>();

// Variables
// -----------------------------------------------------------------------------
const toastID = Math.random().toString(16).substr(2, 8);
let createPermissionModal: Modal | null = null;
let successToast: Toast | null = null;

// Reactive State
// -----------------------------------------------------------------------------
const formState = reactive({
  loading: false,
  error: false,
  readonly: props.readonly,
} as {
  loading: boolean;
  error: boolean;
  readonly: boolean;
});

const permission = reactive({
  from_timestamp: undefined,
  to_timestamp: undefined,
  file_prefix: undefined,
  permission: undefined,
  uid: "",
  bucket_name: props.bucketName,
} as BucketPermission);

// Computes Properties
// -----------------------------------------------------------------------------
const editPermission: ComputedRef<boolean> = computed(
  () => props.editUserPermission != undefined
);

const possibleSubFolders: ComputedRef<string[]> = computed(() =>
  findSubFolders(props.subFolders, [])
);

const permissionUserReadonly: ComputedRef<boolean> = computed(() => {
  return formState.readonly || editPermission.value;
});

// Watchers
// -----------------------------------------------------------------------------
watch(
  () => props.bucketName,
  (newBucketName) => {
    updatePermission();
    permission.bucket_name = newBucketName;
  }
);

watch(
  () => props.editUserPermission,
  () => updatePermission()
);

// Functions
// -----------------------------------------------------------------------------
/**
 * Reset the form. Triggered when the modal is closed.
 */
function modalClosed() {
  formState.readonly = props.readonly;
  formState.error = false;
  if (editPermission.value) {
    updatePermission();
  }
}

/**
 * Check if a input should be visible based on its state
 * @param input Input which visibility should be determined.
 */
function inputVisible(input: string | undefined): boolean {
  return !formState.readonly || input != undefined;
}

/**
 * Update the form content
 */
function updatePermission() {
  if (props.editUserPermission != undefined) {
    permission.bucket_name = props.editUserPermission.bucket_name;
    permission.file_prefix = props.editUserPermission.file_prefix;
    permission.uid = props.editUserPermission.uid;
    permission.from_timestamp =
      props.editUserPermission.from_timestamp != null
        ? dayjs(props.editUserPermission.from_timestamp).format("YYYY-MM-DD")
        : undefined;
    permission.to_timestamp =
      props.editUserPermission.to_timestamp != null
        ? dayjs(props.editUserPermission.to_timestamp).format("YYYY-MM-DD")
        : undefined;
    permission.permission = props.editUserPermission.permission;
  } else {
    permission.file_prefix = undefined;
    permission.uid = "";
    permission.from_timestamp = undefined;
    permission.to_timestamp = undefined;
    permission.permission = undefined;
  }
}

/**
 * Find recursively all sub folders based on the folder structure
 * @param currentFolder Current Folder
 * @param parentFolders All parent folders
 */
function findSubFolders(
  currentFolder: FolderTree,
  parentFolders: string[]
): string[] {
  const arr: string[] = [];
  for (const subFolder of Object.keys(currentFolder.subFolders)) {
    const subFolderString =
      (parentFolders.length > 0 ? parentFolders.join("/") + "/" : "") +
      subFolder +
      "/";
    arr.push(
      subFolderString,
      ...findSubFolders(
        currentFolder.subFolders[subFolder],
        subFolderString.slice(0, subFolderString.length - 1).split("/")
      )
    );
  }
  return arr;
}

/**
 * Submit the form
 */
function formSubmit() {
  formState.error = false;
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  const form = document.getElementById(
    "permissionCreateForm"
  )! as HTMLFormElement;
  if (form.checkValidity()) {
    const tempPermission: BucketPermission = permission;
    if (permission.from_timestamp != null) {
      tempPermission.from_timestamp = dayjs(
        permission.from_timestamp
      ).toISOString();
    }
    if (permission.to_timestamp != null) {
      tempPermission.to_timestamp = dayjs(
        permission.to_timestamp
      ).toISOString();
    }
    formState.loading = true;
    const serverAnswerPromise = editPermission.value
      ? BucketPermissionsService.bucketPermissionsUpdatePermission(
          permission.bucket_name,
          permission.uid,
          {
            to_timestamp: tempPermission.to_timestamp,
            from_timestamp: tempPermission.from_timestamp,
            permission: tempPermission.permission,
            file_prefix: tempPermission.file_prefix,
          } as BucketPermissionParameters
        )
      : BucketPermissionsService.bucketPermissionsCreatePermission(
          tempPermission
        );
    serverAnswerPromise
      .then(() => {
        createPermissionModal?.hide();
        successToast?.show();
      })
      .catch((err) => {
        formState.error = true;
        console.error(err);
      })
      .finally(() => {
        formState.loading = false;
      });
  }
}

// Lifecycle Hooks
// -----------------------------------------------------------------------------
onMounted(() => {
  createPermissionModal = new Modal("#" + props.modalID);
  successToast = new Toast("#" + "toast-" + toastID, { autohide: true });
});
</script>

<template>
  <div class="toast-container position-fixed top-0 end-0 p-3">
    <div
      role="alert"
      aria-live="assertive"
      aria-atomic="true"
      class="toast text-bg-success align-items-center border-0"
      data-bs-autohide="false"
      :id="'toast-' + toastID"
    >
      <div class="d-flex">
        <div class="toast-body">
          Successfully
          <span v-if="editPermission">created</span>
          <span v-else>edited</span>
          Permission
        </div>
        <button
          type="button"
          class="btn-close btn-close-white me-2 m-auto"
          data-bs-dismiss="toast"
          aria-label="Close"
        ></button>
      </div>
    </div>
  </div>
  <bootstrap-modal
    :modalID="modalID"
    :static-backdrop="true"
    :modal-label="modalLabel"
    v-on="{ 'hidden.bs.modal': modalClosed }"
  >
    <template v-slot:header> Create new Permission </template>
    <template v-slot:extra-button v-if="formState.readonly">
      <bootstrap-icon
        icon="pencil-fill"
        :height="15"
        :width="15"
        fill="currentColor"
        class="pseudo-link"
        @click="formState.readonly = false"
      />
    </template>
    <template v-slot:body>
      <form @submit.prevent="formSubmit" id="permissionCreateForm">
        <div class="mb-3 row">
          <label for="bucketNameInput" class="col-2 col-form-label"
            >Bucket<span v-if="!formState.readonly">*</span></label
          >
          <div class="col-10">
            <input
              type="text"
              readonly
              class="form-control-plaintext"
              id="bucketNameInput"
              required
              :value="permission.bucket_name"
            />
          </div>
        </div>
        <div class="mb-3 row">
          <label for="permissionGranteeInput" class="col-2 col-form-label">
            User<span v-if="!formState.readonly">*</span>
          </label>
          <div class="col-10">
            <input
              type="text"
              :class="{
                'form-control-plaintext': permissionUserReadonly,
                'form-control': !permissionUserReadonly,
              }"
              id="permissionGranteeInput"
              required
              minlength="3"
              maxlength="64"
              placeholder="Grantee of the permission"
              v-model.trim="permission.uid"
              :readonly="permissionUserReadonly"
            />
          </div>
        </div>
        <div class="mb-3 row">
          <label for="permissionTypeInput" class="col-2 col-form-label">
            Type<span v-if="!formState.readonly">*</span>
          </label>
          <div class="col-10">
            <select
              class="form-select text-lowercase"
              id="permissionTypeInput"
              required
              :disabled="formState.readonly"
              v-model="permission.permission"
            >
              <option disabled selected>Select one...</option>
              <option v-for="perm in PermissionEnum" :key="perm" :value="perm">
                {{ perm.toLowerCase() }}
              </option>
            </select>
          </div>
        </div>
        <div class="mb-3 row">
          <label
            for="permissionDateFromInput"
            class="col-2 col-form-label"
            v-if="inputVisible(permission.from_timestamp)"
          >
            From
          </label>
          <div class="col-4" v-if="inputVisible(permission.from_timestamp)">
            <input
              type="date"
              class="form-control"
              id="permissionDateFromInput"
              :readonly="formState.readonly"
              :min="dayjs().format('YYYY-MM-DD')"
              v-model="permission.from_timestamp"
            />
          </div>
          <label
            for="permissionDateToInput"
            class="col-2 col-form-label"
            v-if="inputVisible(permission.to_timestamp)"
          >
            To
          </label>
          <div class="col-4" v-if="inputVisible(permission.to_timestamp)">
            <input
              type="date"
              class="form-control"
              id="permissionToFromInput"
              :readonly="formState.readonly"
              v-model="permission.to_timestamp"
              :min="
                permission.from_timestamp != null
                  ? dayjs(permission.from_timestamp)
                      .add(1, 'day')
                      .format('YYYY-MM-DD')
                  : dayjs().add(1, 'day').format('YYYY-MM-DD')
              "
            />
          </div>
        </div>
        <div
          class="mb-3 row"
          v-if="
            inputVisible(permission.file_prefix) &&
            possibleSubFolders.length > 0
          "
        >
          <label for="permissionSubFolderInput" class="col-2 col-form-label">
            Subfolder
          </label>
          <div class="col-10">
            <select
              class="form-select"
              id="permissionSubFolderInput"
              :disabled="formState.readonly"
              v-model="permission.file_prefix"
            >
              <option disabled selected>Select one folder...</option>
              <option
                v-for="folder in possibleSubFolders"
                :key="folder"
                :value="folder"
              >
                {{ folder }}
              </option>
            </select>
          </div>
        </div>
      </form>
      <span class="text-danger" v-if="formState.error"
        >There was some kind of error<br />Try again later</span
      >
    </template>
    <template v-slot:footer>
      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
        Close
      </button>
      <button
        type="submit"
        form="permissionCreateForm"
        class="btn btn-primary"
        :disabled="formState.loading"
        v-if="!formState.readonly"
      >
        <span
          v-if="formState.loading"
          class="spinner-border spinner-border-sm"
          role="status"
          aria-hidden="true"
        ></span>
        Save
      </button>
    </template>
  </bootstrap-modal>
</template>

<style scoped>
.pseudo-link {
  cursor: pointer;
  color: var(--bs-secondary);
}
.pseudo-link:hover {
  color: var(--bs-primary);
}
</style>