<template>
  <v-form ref="form" v-model="valid" :readonly="readonly" @submit.prevent>
    <v-row
      v-for="field in requiredFields"
      :key="field.name"
      :class="{ 'highlight-flash': recentlyVisibleFields.includes(field.name) }"
    >
      <v-col cols="6">
        <dynamic-form-input
          v-model="form[field.name]"
          :suggested-values="suggestedValuesForField(field)"
          :strict="strictForField(field)"
          :name="field.name"
          :rules="rules[field.name]"
          :type="field.type"
        />
      </v-col>
      <v-col cols="6">
        <v-list-subheader>{{ field.description }}</v-list-subheader>
      </v-col>
    </v-row>

    <v-list-subheader v-if="optionalFields.length > 0"
      >Optional Fields</v-list-subheader
    >
    <v-divider v-if="optionalFields.length > 0" class="mb-8" />
    <v-row
      v-for="field in optionalFields"
      :key="field.name"
      :class="{ 'highlight-flash': recentlyVisibleFields.includes(field.name) }"
    >
      <v-col cols="6">
        <dynamic-form-input
          v-model="form[field.name]"
          :suggested-values="suggestedValuesForField(field)"
          :strict="strictForField(field)"
          :name="field.name"
          :rules="rules[field.name]"
          :type="field.type"
        />
      </v-col>
      <v-col cols="6">
        <v-list-subheader>{{ field.description }}</v-list-subheader>
      </v-col>
    </v-row>
  </v-form>
</template>
<script>
import { useListenerStore } from "@/stores/listener-module";
import { useBypassStore } from "@/stores/bypass-module";
import { useCredentialStore } from "@/stores/credential-module";
import { useMalleableProfileStore } from "@/stores/malleable-module";
import { useAgentStore } from "@/stores/agent-module";
import DynamicFormInput from "./DynamicFormInput.vue";

export default {
  components: {
    DynamicFormInput,
  },
  inheritAttrs: false,
  props: {
    modelValue: {
      type: Object,
      default: () => ({}),
    },
    options: {
      type: Object,
      required: true,
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    priority: {
      type: Array,
      default: () => [],
    },
  },
  emits: ["update:modelValue"],
  data() {
    return {
      form: {},
      valid: true,
      visibleFields: {},
      recentlyVisibleFields: [],
      initializedBypassesDefaults: false,
    };
  },
  computed: {
    agentStore() {
      return useAgentStore();
    },
    listenerStore() {
      return useListenerStore();
    },
    bypassStore() {
      return useBypassStore();
    },
    credentialStore() {
      return useCredentialStore();
    },
    malleableProfileStore() {
      return useMalleableProfileStore();
    },
    agents() {
      return this.agentStore.agents;
    },
    listeners() {
      return this.listenerStore.listenerNames;
    },
    bypasses() {
      // Use defaults first, then remaining bypass names
      return this.bypassStore.mergedBypassNames;
    },
    defaultBypasses() {
      return this.bypassStore.defaultBypassNames;
    },
    credentials() {
      return this.credentialStore.credentials;
    },
    malleableProfiles() {
      return this.malleableProfileStore.profileNames;
    },
    optionalFields() {
      return this.allFields.filter(
        (el) => el.required === false && this.isFieldVisible(el),
      );
    },
    requiredFields() {
      return this.allFields.filter(
        (el) => el.required === true && this.isFieldVisible(el),
      );
    },
    allFields() {
      const fields = Object.keys(this.options).map((key) => ({
        name: key,
        ...this.options[key],
      }));

      this.priority
        .slice()
        .reverse()
        .forEach((item) => {
          const index = fields.findIndex((f) => f.name === item);
          if (index > -1) {
            const fItem = fields.splice(index, 1)[0];
            fields.unshift(fItem);
          }
        });

      return fields.map((el) => ({
        ...el,
        type: this.fieldType(el),
      }));
    },
    rules() {
      return this.allFields.reduce((map, field) => {
        if (this.isFieldVisible(field)) {
          map[field.name] = [];
          if (field.required === true) {
            map[field.name].push((v) => !!v || `${field.name} is required`);
          }
        }
        return map;
      }, {});
    },
  },
  watch: {
    options: {
      immediate: true,
      handler(newOptions) {
        this.initializeForm(newOptions);
        this.updateFieldVisibility(this.form, true);
      },
    },
    form: {
      handler(val) {
        this.updateFieldVisibility(val);
        const updatedForm = { ...val };

        if (updatedForm.Bypasses) {
          updatedForm.Bypasses = updatedForm.Bypasses.join(" ");
        }

        this.$emit("update:modelValue", updatedForm);
      },
      deep: true,
    },
    allFields: {
      immediate: true,
      handler(arr) {
        const map2 = arr.reduce((map, obj) => {
          if (obj.name === "Bypasses") {
            if (Array.isArray(obj.value)) {
              map[obj.name] = obj.value;
            } else {
              const raw = obj.value == null ? "" : String(obj.value);
              map[obj.name] = raw
                .split(" ")
                .map((s) => s.trim())
                .filter((s) => s.length > 0);
            }
          } else {
            map[obj.name] = obj.value == null ? "" : obj.value;
          }
          return map;
        }, {});
        this.form = map2;
      },
    },
    defaultBypasses: {
      immediate: true,
      handler() {
        // When defaults arrive, initialize if needed
        this.tryInitializeBypassesDefaults();
      },
    },
  },
  mounted() {
    this.agentStore.getAgents();
    this.listenerStore.getListeners();
    this.bypassStore.getBypasses();
    this.malleableProfileStore.getMalleableProfiles();
    this.credentialStore.getCredentials();
  },
  methods: {
    async validate() {
      const { valid } = await this.$refs.form.validate();
      return valid;
    },
    initializeForm(options) {
      this.form = {};
      Object.keys(options).forEach((key) => {
        this.form[key] = options[key].value || null;
      });
    },
    tryInitializeBypassesDefaults() {
      if (this.initializedBypassesDefaults) return;
      if (
        !this.form ||
        !Object.prototype.hasOwnProperty.call(this.form, "Bypasses")
      )
        return;
      const current = this.form.Bypasses;
      const isEmpty =
        current == null ||
        (Array.isArray(current) && current.length === 0) ||
        (typeof current === "string" && current.trim() === "");
      if (!isEmpty) return;
      if (!this.defaultBypasses || this.defaultBypasses.length === 0) return;
      this.form.Bypasses = [...this.defaultBypasses];
      this.initializedBypassesDefaults = true;
    },
    updateFieldVisibility(form, initial = false) {
      const newlyVisibleFields = [];
      const oldVisibleFields = { ...this.visibleFields };
      this.visibleFields = {};
      this.allFields.forEach((field) => {
        if (field.depends_on && Array.isArray(field.depends_on)) {
          const shouldBeVisible = field.depends_on.every((dependency) =>
            dependency.values.includes(form[dependency.name]),
          );
          if (shouldBeVisible && !oldVisibleFields[field.name]) {
            if (!initial) newlyVisibleFields.push(field.name);
          }
          this.visibleFields[field.name] = shouldBeVisible;
        } else {
          if (!this.visibleFields[field.name]) {
            if (!initial) newlyVisibleFields.push(field.name);
          }
          this.visibleFields[field.name] = true;
        }
      });

      this.highlightNewlyVisibleFields(newlyVisibleFields);
    },
    highlightNewlyVisibleFields(fields) {
      this.recentlyVisibleFields = fields;
      setTimeout(() => {
        this.recentlyVisibleFields = [];
      }, 7000);
    },
    isFieldVisible(field) {
      return this.visibleFields[field.name] !== false;
    },
    suggestedValuesForField(field) {
      if (field.name === "Agent") return this.agents;
      if (["Listener", "RedirectListener"].includes(field.name))
        return this.listeners;
      if (field.name === "Bypasses") return this.bypasses;
      if (field.name === "Profile") return this.malleableProfiles;
      if (field.name === "CredID") return this.credentials;
      return field.suggested_values;
    },
    strictForField(field) {
      return ["Listener", "Bypasses", "Profile", "CredID"].includes(field.name)
        ? true
        : field.strict;
    },
    fieldType(el) {
      return (
        {
          INTEGER: "number",
          FLOAT: "float",
          BOOLEAN: "boolean",
          STRING: "string",
          FILE: "file",
        }[el.value_type] || "string"
      );
    },
  },
};
</script>

<style scoped>
.highlight-flash {
  animation: flash 1s ease-in-out;
}

@keyframes flash {
  0% {
  }
  50% {
    opacity: 1;
    background-color: #676767;
    box-shadow: 0 0 5px 5px #676767;
  }
  100% {
  }
}
</style>
