<script setup lang="ts">
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { onMounted, reactive, computed } from "vue";
import type { WorkflowExecutionOut } from "@/client/workflow";
import { useAuthStore } from "@/stores/auth";
import {
  WorkflowExecutionService,
  WorkflowExecutionStatus,
  WorkflowService,
  WorkflowVersionService,
} from "@/client/workflow";
import dayjs from "dayjs";
import DeleteModal from "@/components/modals/DeleteModal.vue";

const userStore = useAuthStore();

const executionsState = reactive<{
  workflowMapping: Record<string, string>;
  versionMapping: Record<string, string>;
  workflowExecutions: WorkflowExecutionOut[];
  loading: boolean;
  mappingLoading: boolean;
  executionToDelete?: WorkflowExecutionOut;
}>({
  workflowMapping: {},
  versionMapping: {},
  workflowExecutions: [],
  loading: true,
  mappingLoading: true,
  executionToDelete: undefined,
});

const statusToColorMapping = {
  PENDING: "bg-warning",
  SCHEDULED: "bg-warning",
  RUNNING: "bg-info",
  CANCELED: "bg-danger",
  SUCCESS: "bg-success",
  ERROR: "bg-danger",
};

const statusToIconMapping = {
  PENDING: "fa-solid fa-circle-pause",
  SCHEDULED: "fa-solid fa-circle-pause",
  RUNNING: "fa-solid fa-circle-play",
  CANCELED: "fa-solid fa-circle-xmark",
  SUCCESS: "fa-solid fa-circle-check",
  ERROR: "fa-solid fa-circle-xmark",
};

const sortedExecutions = computed<WorkflowExecutionOut[]>(() => {
  const tempList = [...executionsState.workflowExecutions];
  tempList.sort((a, b) => {
    // sort by start time descending
    return dayjs(a.start_time).isBefore(dayjs(b.start_time)) ? 1 : -1;
  });
  return tempList;
});

const deleteModalString = computed<string>(() => {
  if (executionsState.executionToDelete === undefined) {
    return "";
  } else if (
    !executionsState.executionToDelete.workflow_version_id ||
    !executionsState.executionToDelete.workflow_id
  ) {
    return `Workflow Execution from ${dayjs(
      executionsState.executionToDelete.start_time
    ).format("DD.MM.YYYY HH:mm")}`;
  } else {
    return `Workflow Execution ${
      executionsState.workflowMapping[
        executionsState.executionToDelete.workflow_id
      ]
    }@${
      executionsState.versionMapping[
        executionsState.executionToDelete.workflow_version_id
      ]
    } from ${dayjs(executionsState.executionToDelete.start_time).format(
      "DD.MM.YYYY HH:mm"
    )}`;
  }
});

// Functions
// -----------------------------------------------------------------------------
function updateExecutions() {
  const listExecutionsPromise =
    WorkflowExecutionService.workflowExecutionListWorkflowExecutions(
      userStore.currentUID
    )
      .then((executions) => {
        executionsState.workflowExecutions = executions;
        return executions;
      })
      .finally(() => {
        executionsState.loading = false;
      });
  listExecutionsPromise // construct mapping from workflow id to workflow name
    .then((executions) =>
      Promise.all(
        // group all calls to the API
        executions
          .filter((execution) => execution.workflow_id) // filter undefined workflows
          .filter(
            (execution) =>
              !executionsState.workflowMapping[execution.workflow_id]
          )
          .filter(
            // filter unique workflows
            (execution, index, array) =>
              array.findIndex(
                (val) => val.workflow_id === execution.workflow_id
              ) === index
          )
          .map((execution) =>
            WorkflowService.workflowGetWorkflow(execution.workflow_id)
          )
      )
    )
    .then((workflows) =>
      workflows.forEach((workflow) => {
        executionsState.workflowMapping[workflow.workflow_id] = workflow.name;
      })
    )
    .finally(() => {
      executionsState.mappingLoading = false;
    });
  listExecutionsPromise // construct mapping from version id to clear text version
    .then((executions) =>
      Promise.all(
        // group all calls to the API
        executions
          .filter(
            // filter undefined workflow versions
            (execution) =>
              execution.workflow_id && execution.workflow_version_id
          )
          .filter(
            // filter already seen workflow versions
            (version) => !executionsState.versionMapping[version.workflow_id]
          )
          .filter(
            // filter unique workflow versions
            (execution, index, array) =>
              array.findIndex(
                (val) =>
                  val.workflow_version_id === execution.workflow_version_id
              ) === index
          )
          .map((execution) =>
            WorkflowVersionService.workflowVersionGetWorkflowVersion(
              execution.workflow_version_id,
              execution.workflow_id
            )
          )
      )
    )
    .then((versions) =>
      versions.forEach((version) => {
        executionsState.versionMapping[version.git_commit_hash] =
          version.version;
      })
    );
}

function workflowExecutionDeletable(status: WorkflowExecutionStatus): boolean {
  return [
    WorkflowExecutionStatus.ERROR,
    WorkflowExecutionStatus.CANCELED,
    WorkflowExecutionStatus.SUCCESS,
  ].includes(status);
}
function workflowExecutionCancable(status: WorkflowExecutionStatus): boolean {
  return [
    WorkflowExecutionStatus.RUNNING,
    WorkflowExecutionStatus.PENDING,
    WorkflowExecutionStatus.SCHEDULED,
  ].includes(status);
}

function deleteWorkflowExecution(executionId?: string) {
  if (executionId) {
    WorkflowExecutionService.workflowExecutionDeleteWorkflowExecution(
      executionId
    ).then(() => {
      executionsState.workflowExecutions =
        executionsState.workflowExecutions.filter(
          (execution) => execution.execution_id !== executionId
        );
    });
  }
}

function cancelWorkflowExecution(executionId: string) {
  WorkflowExecutionService.workflowExecutionCancelWorkflowExecution(
    executionId
  ).then(() => {
    const index = executionsState.workflowExecutions.findIndex(
      (execution) => execution.execution_id === executionId
    );
    if (index > -1) {
      executionsState.workflowExecutions[index].status =
        WorkflowExecutionStatus.CANCELED;
      executionsState.workflowExecutions[index].end_time =
        dayjs().toISOString();
    }
  });
}

onMounted(() => {
  updateExecutions();
});
</script>

<template>
  <delete-modal
    modal-i-d="deleteWorkflowExecutionModal"
    :object-name-delete="deleteModalString"
    @confirm-delete="
      deleteWorkflowExecution(executionsState.executionToDelete?.execution_id)
    "
  />
  <div
    class="row m-2 border-bottom border-light mb-4 justify-content-between align-items-center"
  >
    <h1 class="mb-2 text-light w-fit">My Workflow Executions</h1>
    <router-link :to="{ name: 'workflows' }" class="btn btn-primary w-fit"
      >Start Workflow Execution</router-link
    >
  </div>
  <table
    class="table table-dark table-striped table-hover caption-top align-middle"
  >
    <caption>
      Displaying
      {{
        executionsState.workflowExecutions.length
      }}
      Workflow Execution
    </caption>
    <thead>
      <tr>
        <th scope="col">Workflow</th>
        <th scope="col">Status</th>
        <th scope="col">Started</th>
        <th scope="col">Ended</th>
        <th scope="col"></th>
      </tr>
    </thead>
    <tbody>
      <template v-if="executionsState.loading">
        <tr v-for="n in 5" :key="n">
          <td class="placeholder-glow w-25">
            <span class="placeholder col-6"></span>
          </td>
          <td class="placeholder-glow" style="width: 20%">
            <span class="placeholder col-4"></span>
          </td>
          <td class="placeholder-glow" style="width: 20%">
            <span class="placeholder col-6"></span>
          </td>
          <td class="placeholder-glow" style="width: 20%">
            <span class="placeholder col-6"></span>
          </td>
          <td class="text-end">
            <div
              class="btn-group btn-group-sm dropdown-center dropdown-menu-start"
            >
              <button type="button" class="btn btn-secondary" disabled>
                Details
              </button>
              <button
                type="button"
                class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
                disabled
              >
                <span class="visually-hidden">Toggle Dropdown</span>
              </button>
            </div>
          </td>
        </tr>
      </template>
      <template v-else-if="executionsState.workflowExecutions.length > 0">
        <tr v-for="execution in sortedExecutions" :key="execution.execution_id">
          <td
            v-if="executionsState.mappingLoading"
            class="placeholder-glow w-25"
          >
            <span class="placeholder col-6"></span>
          </td>
          <td v-else>
            <span>{{
              executionsState.workflowMapping[execution.workflow_id]
            }}</span>
            <span
              >@{{
                executionsState.versionMapping[execution.workflow_version_id]
              }}</span
            >
          </td>
          <td>
            <span
              class="rounded-pill py-1 px-2"
              :class="statusToColorMapping[execution.status]"
              ><font-awesome-icon
                class="me-2"
                :icon="statusToIconMapping[execution.status]"
              />{{ execution.status }}</span
            >
          </td>
          <td>{{ dayjs(execution.start_time).format("DD.MM.YYYY HH:mm") }}</td>
          <td>
            <template v-if="execution.end_time">
              {{ dayjs(execution.end_time).format("DD.MM.YYYY HH:mm") }}
            </template>
            <template v-else> - </template>
          </td>
          <td class="text-end">
            <div
              class="btn-group btn-group-sm dropdown-center dropdown-menu-start"
            >
              <button type="button" class="btn btn-secondary">Details</button>
              <button
                type="button"
                class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
                data-bs-toggle="dropdown"
                aria-expanded="false"
              >
                <span class="visually-hidden">Toggle Dropdown</span>
              </button>
              <ul class="dropdown-menu dropdown-menu-dark">
                <li v-if="workflowExecutionCancable(execution.status)">
                  <button
                    class="dropdown-item text-danger align-middle"
                    type="button"
                    @click="cancelWorkflowExecution(execution.execution_id)"
                  >
                    <font-awesome-icon icon="fa-solid fa-ban" />
                    <span class="ms-1">Cancel</span>
                  </button>
                </li>
                <li v-if="workflowExecutionDeletable(execution.status)">
                  <button
                    class="dropdown-item text-danger align-middle"
                    type="button"
                    data-bs-toggle="modal"
                    data-bs-target="#deleteWorkflowExecutionModal"
                    @click="executionsState.executionToDelete = execution"
                  >
                    <font-awesome-icon icon="fa-solid fa-trash" />
                    <span class="ms-1">Delete</span>
                  </button>
                </li>
              </ul>
            </div>
          </td>
        </tr>
      </template>
      <tr v-else>
        <td colspan="5" class="text-center"><i>No workflow executions</i></td>
      </tr>
    </tbody>
  </table>
</template>

<style scoped></style>