<template>
    <div class="frappe-form">
        <form-actions
          class="p-3 border-bottom"
          v-if="shouldRenderForm"
          :doc="doc"
          :links="links"
          @save="save"
          @submit="submit"
          @revert="revert"
          @print="print"
        />
        <form-layout
          class="p-3"
          v-if="shouldRenderForm"
          :doc="doc"
          :fields="meta.fields"
          :layout="meta.layout"
          :invalid="invalid"
        />
        <not-found v-if="notFound" />
    </div>
</template>
<script>
import frappe from 'frappejs';
import FormLayout from './FormLayout';
import FormActions from './FormActions';
import { _ } from 'frappejs/utils';

export default {
  name: 'Form',
  props: ['doctype', 'name', 'defaultValues'],
  components: {
    FormActions,
    FormLayout
  },
  data() {
    return {
      docLoaded: false,
      notFound: false,
      invalid: false,
      invalidFields: [],
      links: []
    };
  },
  computed: {
    meta() {
      return frappe.getMeta(this.doctype);
    },
    shouldRenderForm() {
      return this.name && this.docLoaded;
    }
  },
  async created() {
    if (!this.name) return;
    try {
      this.doc = await frappe.getDoc(this.doctype, this.name);

      if (this.doc._notInserted && this.meta.fields.map(df => df.fieldname).includes('name')) {
        // For a user editable name field,
        // it should be unset since it is autogenerated
        this.doc.set('name', '');
      }

      if (this.defaultValues) {
        for (let fieldname in this.defaultValues) {
          const value = this.defaultValues[fieldname];
          this.doc.set(fieldname, value);
        }
      }

      this.docLoaded = true;
    } catch (e) {
      this.notFound = true;
    }
    this.setLinks();
    this.doc.on('change', this.setLinks);
  },
  methods: {
    async save() {
      this.setValidity();
      if (this.invalid) return;

      try {
        if (this.doc._notInserted) {
          await this.doc.insert();
        } else {
          await this.doc.update();
        }

        this.$emit('save', this.doc);

      } catch (e) {
        console.error(e);
        return;
      }
    },

    setLinks() {
      if (this.meta.links) {
        let links = [];
        for (let link of this.meta.links) {
          if (link.condition(this)) {
            link.handler = () => {
              link.action(this);
            };
            links.push(link);
          }
        }
        this.links = links;
      }
    },

    async submit() {
      this.doc.set('submitted', 1);
      await this.save();
    },

    async revert() {
      this.doc.set('submitted', 0);
      await this.save();
    },

    print() {
      this.$router.push(`/print/${this.doctype}/${this.name}`);
    },

    onValidate(fieldname, isValid) {
      if (!isValid && !this.invalidFields.includes(fieldname)) {
        this.invalidFields.push(fieldname);
      } else if (isValid) {
        this.invalidFields = this.invalidFields.filter(
          invalidField => invalidField !== fieldname
        );
      }
    },

    setValidity() {
      const form = this.$el.querySelector('form');
      let validity = form.checkValidity();
      this.invalid = !validity;
    }
  }
};
</script>
<style>
</style>
