<script lang="ts" setup>
import SubmitButton from "@/components/button/SubmitButton.vue";
import { setFocus } from "@/formkit/utils/focus";
import { useSettingFormConvert } from "@console/composables/use-setting-form";
import type { Policy } from "@halo-dev/api-client";
import { coreApiClient } from "@halo-dev/api-client";
import { Toast, VButton, VLoading, VModal, VSpace } from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { cloneDeep } from "lodash-es";
import { computed, onMounted, ref, toRaw, toRefs } from "vue";
import { useI18n } from "vue-i18n";

const props = withDefaults(
  defineProps<{
    policy?: Policy;
    templateName?: string;
  }>(),
  {
    policy: undefined,
    templateName: undefined,
  }
);

const { policy } = toRefs(props);

const emit = defineEmits<{
  (event: "close"): void;
}>();

const { t } = useI18n();

const modal = ref<InstanceType<typeof VModal> | null>(null);

const formState = ref<Policy>({
  spec: {
    displayName: "",
    templateName: "",
    configMapName: "",
  },
  apiVersion: "storage.halo.run/v1alpha1",
  kind: "Policy",
  metadata: {
    name: "",
    generateName: "attachment-policy-",
  },
});

const isUpdateMode = !!props.policy;

onMounted(async () => {
  if (props.policy) {
    formState.value = cloneDeep(props.policy);
  }
  if (props.templateName) {
    formState.value.spec.templateName = props.templateName;
  }

  setFocus("displayNameInput");
});

const { data: policyTemplate } = useQuery({
  queryKey: [
    "core:attachment:policy-template",
    formState.value.spec.templateName,
  ],
  cacheTime: 0,
  queryFn: async () => {
    const { data } =
      await coreApiClient.storage.policyTemplate.getPolicyTemplate({
        name: formState.value.spec.templateName,
      });
    return data;
  },
  retry: 0,
  enabled: computed(() => !!formState.value.spec.templateName),
});

const { data: setting, isLoading } = useQuery({
  queryKey: [
    "core:attachment:policy-template:setting",
    policyTemplate.value?.spec?.settingName,
  ],
  cacheTime: 0,
  queryFn: async () => {
    if (!policyTemplate.value?.spec?.settingName) {
      throw new Error("No setting found");
    }

    const { data } = await coreApiClient.setting.getSetting({
      name: policyTemplate.value.spec.settingName,
    });

    return data;
  },
  retry: 0,
  enabled: computed(() => !!policyTemplate.value?.spec?.settingName),
});

const { data: configMap } = useQuery({
  queryKey: [
    "core:attachment:policy-template:configMap",
    policy.value?.spec.configMapName,
  ],
  cacheTime: 0,
  initialData: {
    data: {},
    apiVersion: "v1alpha1",
    kind: "ConfigMap",
    metadata: {
      generateName: "configMap-",
      name: "",
    },
  },
  retry: 0,
  queryFn: async () => {
    if (!policy.value?.spec.configMapName) {
      throw new Error("No configMap found");
    }
    const { data } = await coreApiClient.configMap.getConfigMap({
      name: policy.value?.spec.configMapName,
    });
    return data;
  },
  enabled: computed(() => !!policy.value?.spec.configMapName),
});

const { configMapFormData, formSchema, convertToSave } = useSettingFormConvert(
  setting,
  configMap,
  ref("default")
);

const submitting = ref(false);

const handleSave = async () => {
  try {
    submitting.value = true;
    const configMapToUpdate = convertToSave();
    if (isUpdateMode) {
      await coreApiClient.configMap.updateConfigMap({
        name: configMap.value.metadata.name,
        configMap: configMapToUpdate,
      });

      await coreApiClient.storage.policy.updatePolicy({
        name: formState.value.metadata.name,
        policy: formState.value,
      });
    } else {
      const { data: policies } =
        await coreApiClient.storage.policy.listPolicy();
      const hasDisplayNameDuplicate = policies.items.some(
        (policy) => policy.spec.displayName === formState.value.spec.displayName
      );

      if (hasDisplayNameDuplicate) {
        Toast.error(
          t("core.attachment.policy_editing_modal.toast.policy_name_exists")
        );
        return;
      }
      const { data: newConfigMap } =
        await coreApiClient.configMap.createConfigMap({
          configMap: configMapToUpdate,
        });

      formState.value.spec.configMapName = newConfigMap.metadata.name;

      await coreApiClient.storage.policy.createPolicy({
        policy: formState.value,
      });
    }

    Toast.success(t("core.common.toast.save_success"));
    modal.value?.close();
  } catch (e) {
    console.error("Failed to save attachment policy", e);
  } finally {
    submitting.value = false;
  }
};

const modalTitle = props.policy
  ? t("core.attachment.policy_editing_modal.titles.update", {
      policy: props.policy?.spec.displayName,
    })
  : t("core.attachment.policy_editing_modal.titles.create", {
      policy_template: policyTemplate.value?.spec?.displayName,
    });
</script>
<template>
  <VModal
    ref="modal"
    mount-to-body
    :title="modalTitle"
    :width="600"
    @close="emit('close')"
  >
    <div>
      <VLoading v-if="isLoading" />
      <template v-else>
        <FormKit
          v-if="formSchema && configMapFormData"
          id="attachment-policy-form"
          v-model="configMapFormData['default']"
          name="attachment-policy-form"
          :actions="false"
          :preserve="true"
          type="form"
          :config="{ validationVisibility: 'submit' }"
          @submit="handleSave"
        >
          <FormKit
            id="displayNameInput"
            v-model="formState.spec.displayName"
            :label="
              $t(
                'core.attachment.policy_editing_modal.fields.display_name.label'
              )
            "
            type="text"
            name="displayName"
            validation="required|length:0,50"
          ></FormKit>
          <FormKitSchema
            :schema="toRaw(formSchema)"
            :data="configMapFormData['default']"
          />
        </FormKit>
      </template>
    </div>

    <template #footer>
      <VSpace>
        <SubmitButton
          :loading="submitting"
          type="secondary"
          :text="$t('core.common.buttons.submit')"
          @submit="$formkit.submit('attachment-policy-form')"
        >
        </SubmitButton>
        <VButton @click="modal?.close()">
          {{ $t("core.common.buttons.cancel_and_shortcut") }}
        </VButton>
      </VSpace>
    </template>
  </VModal>
</template>
