<template>
  <div class="works">
    <div class="add-menu" :style="materialMenuStyle" ref="materialMenu" @blur="materialMenuStyle.display = 'none'" tabindex="0">
      <input type="text" v-model.trim="materialMenuSearch" placeholder="Nosaukums..." ref="materialMenuSearch" @blur="materialMenuStyle.display = 'none'">
      <ul>
        <li v-for="item in filteredMenuMaterials" @click="updateWithMaterial(item.id)" :key="item.id">
          {{ item.name }} <span class="amount">({{ item.amount }})</span>
        </li>
      </ul>
    </div>
    <div class="add-menu" :style="mechanismMenuStyle" ref="mechanismMenu" @blur="mechanismMenuStyle.display = 'none'" tabindex="0">
      <input type="text" v-model.trim="mechanismMenuSearch" placeholder="Nosaukums..." ref="mechanismMenuSearch" @blur="mechanismMenuStyle.display = 'none'">
      <ul>
        <li v-for="item in filteredMenuMechanisms" @click="updateWithMechanism(item.id)" :key="item.id">
          {{ item.name }} <span class="amount">({{ item.amount }})</span>
        </li>
      </ul>
    </div>
    <div class="add-section">
      <div>
        <div class="add-group-wrap">
          <div class="add-group" v-for="(item, index) in work" :key="index">
            <input type="text" v-model.trim="item.name" placeholder="Nosaukums" />
            <input type="text" v-model.trim="item.code" placeholder="Kods" />
            <input type="text" v-model.trim="item.unit_of_measurement" placeholder="Mērvienība" />
            <input type="number" v-model.trim="item.amount" placeholder="Daudzums" />
            <input type="number" v-model.trim="item.time_norm" placeholder="Laika norma (c/h)" />
            <input type="number" v-model.trim="item.pay_rate" placeholder="Darba samaksas likme (EUR/h)" />
            <button class="btn-round-big btn-red" v-show="work.length > 1" @click="minusWork(index)">-</button>
          </div>
        </div>
        <button class="btn-round-big btn-blue" @click="addWork">+</button>
        <button class="btn btn-green border-def" @click="createWork">Pievienot</button>
      </div>
      <div class="error-block" v-if="errors.createWork.display" style="display: block;" @click="hideError('createWork')"><span>{{ errors.createWork.message }}</span></div>
      <div class="search-group-wrap">
        <div class="search-group" v-for="(field, index) in search" :key="index">
          <input type="text" v-model.trim="field.query" placeholder="Meklēt...">
          <select v-model="field.property" v-if="worksWithSums.length > 0">
            <option v-for="(key, index) in Object.keys(worksWithSums[0]).filter(x => x !== 'materials' && x !== 'mechanisms')" :key="index" :value="key">{{ properties[key] }}</option>
          </select>
          <button class="btn-round-big btn-red" v-show="search.length > 1" @click="removeQuery(index)">-</button>
        </div>
      </div>
      <button class="btn-round-big btn-blue" @click="addQuery">+</button>
    </div>
    <div v-if="works.length > 0">
      <div class="overflow-x-wrapper">
        <table class="main">
          <tr>
            <th>
              <input
              type="checkbox"
              v-model="checkedAll"
              @change="checkedAll ? checkedItems = filtered.map(x => x.id) : checkedItems = []">
            </th>
            <th
            class="sortable"
            @click="changeSort('name')"
            :class="{ sorted: sort.property === 'name' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Nosaukums
            </th>
            <th
            class="sortable"
            @click="changeSort('code')"
            :class="{ sorted: sort.property === 'code' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Kods
            </th>
            <th
            class="sortable"
            @click="changeSort('unit_of_measurement')"
            :class="{ sorted: sort.property === 'unit_of_measurement' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Mērvienība
            </th>
            <th
            class="sortable"
            @click="changeSort('amount')"
            :class="{ sorted: sort.property === 'amount' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Daudzums
            </th>
            <th
            class="sortable"
            @click="changeSort('time_norm')"
            :class="{ sorted: sort.property === 'time_norm' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Laika norma (c/h)
            </th>
            <th
            class="sortable"
            @click="changeSort('pay_rate')"
            :class="{ sorted: sort.property === 'pay_rate' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Darba samaksas likme (EUR/h)
            </th>
            <th>Materiāli</th>
            <th>Mehānismi</th>
            <th
            class="heading-light sortable"
            @click="changeSort('salary')"
            :class="{ sorted: sort.property === 'salary' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Darba alga (EUR)
            </th>
            <th
            class="heading-light sortable"
            @click="changeSort('sum')"
            :class="{ sorted: sort.property === 'sum' && !sort.disabled, asc: sort.asc, desc: !sort.asc }">
              Summa (EUR)
            </th>
          </tr>
          <tr v-for="item in filtered" :key="item.id">
            <td>
              <input
              type="checkbox"
              :value="item.id"
              v-model="checkedItems"
              @change="checkedItems.length === filtered.length ? checkedAll = true : checkedAll = false">
            </td>
            <template v-if="!checkedItems.includes(item.id)">
              <td>
                <router-link :to="{ name: 'Work', params: { id: item.id } }">
                  {{ !edited[item.id] ? item.name : edited[item.id].name || item.name }}
                </router-link>
                <button class="copy-btn" @click="copy(item.id)">Kopēt</button>
              </td>
              <td v-for="(key, index) in ['code', 'unit_of_measurement', 'amount', 'time_norm', 'pay_rate']" :key="index">
                {{ !edited[item.id] ? item[key] : edited[item.id][key] || item[key] }}
                <button class="copy-btn" @click="copy(item[key])">Kopēt</button>
              </td>
            </template>
            <template v-else>
              <td v-for="(key, index) in ['name', 'code', 'unit_of_measurement', 'amount', 'time_norm', 'pay_rate']" :key="index">
                <input
                :type="typeof item[key] === 'number' ? 'number' : 'text'"
                :value="!edited[item.id] ? item[key] : edited[item.id][key] || item[key]"
                @input="addToEdited(item, key, $event.target.value)">
              </td>
            </template>
            <td>
              <div v-for="(material, index) in item.materials" class="added-item" :key="index">
                <router-link :to="{ name: 'Material', params: { id: material.id } }">{{ material.name }}</router-link>
                <button
                class="btn-round-small btn-red hover-3"
                style="margin-left: 4px; flex: none;"
                @click="removeMaterial(item, material.id)">
                  &#x00D7;
                </button>
              </div>
              <button
              class="btn-round-small btn-blue"
              v-show="materials.length > 0 && materials && (!item.materials || item.materials.length < materials.length)"
              @click="showMaterialMenu($event, item)">
                +
              </button>
            </td>
            <td>
              <div v-for="(mechanism, index) in item.mechanisms" class="added-item" :key="index">
                <router-link :to="{ name: 'Mechanism', params: { id: mechanism.id } }">{{ mechanism.name }}</router-link>
                <button
                class="btn-round-small btn-red hover-3"
                style="margin-left: 4px; flex: none;"
                @click="removeMechanism(item, mechanism.id)">
                  &#x00D7;
                </button>
              </div>
              <button
              class="btn-round-small btn-blue"
              v-show="mechanisms.length > 0 && mechanisms && (!item.mechanisms || item.mechanisms.length < mechanisms.length)"
              @click="showMechanismMenu($event, item)">
                +
              </button>
            </td>
            <td>
              {{ item.salary }}
              <button class="copy-btn" @click="copy(item.salary)">Kopēt</button>
            </td>
            <td>
              {{ item.sum }}
              <button class="copy-btn" @click="copy(item.sum)">Kopēt</button>
            </td>
          </tr>
        </table>
      </div>
      <button
      class="btn btn-green border"
      @click="updateWork"
      :disabled="checkedItems.length === 0">
        Saglabāt
      </button>
      <button
      class="btn btn-yellow border"
      @click="edited = {}; checkedItems = []; checkedAll = false"
      :disabled="checkedItems.length === 0">
        Atcelt
      </button>
      <button
      class="btn btn-red border"
      @click="deleteWork(checkedItems)"
      :disabled="checkedItems.length === 0">
        Dzēst
      </button>
      <button
      class="btn btn-blue border"
      @click="$router.push({ name: 'Estimate', query: { works: checkedItems } })"
      :disabled="checkedItems.length === 0">
        Uz tāmi
      </button>
      <div class="dropdown" v-if="works.length > 0">
        <button class="btn btn-green border" @click="generateForExport">
          Eksportēt
        </button>
        <ul class="dropdown-data" v-if="worksForExport.length > 0">
          <a
          :href="'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(worksForExport))"
          :download="'works-' + Date.now() + '.json'">
            <li>
              JSON
            </li>
          </a>
          <a
          :href="'data:text/csv;charset=utf-8,' + encodeURIComponent(worksToCSV())"
          :download="'works-' + Date.now() + '.csv'">
            <li>
              CSV
            </li>
          </a>
        </ul>
      </div>
      <div class="dropdown">
        <button class="btn btn-green border" @click="openImportDropdown($event.target)" ref="importMenu">
          Importēt
        </button>
        <div class="dropdown-data" :style="dropdownStyle" @click="$refs.importMenu.focus()">
          <div class="tab-bar">
            <button
            v-for="(tab, index) in tabs.import.tabs"
            :class="{ 'active': tabs.import.tabs.active === index }"
            @click="$set(tabs.import.tabs, 'active', index)"
            :key="index">
              {{ tab }}
            </button>
          </div>
          <div class="tab-data" v-show="tabs.import.tabs.active === 0">
            <input type="file" ref="uploadJSON" accept=".json">
            <div class="error-block" v-if="errors.uploadJSON.display" @click="hideError('uploadJSON')"><span>{{ errors.uploadJSON.message }}</span></div>
            <button class="btn btn-green border" @click="uploadJSON()">
              Ielādēt
            </button>
          </div>
          <div class="tab-data" v-show="tabs.import.tabs.active === 1">
            <input type="file" ref="uploadCSV" accept=".csv">
            <div class="error-block" v-if="errors.uploadCSV.display" @click="hideError('uploadCSV')"><span>{{ errors.uploadCSV.message }}</span></div>
            <button class="btn btn-green border" @click="uploadCSV()">
              Ielādēt
            </button>
          </div>
        </div>
      </div>
    </div>
    <div v-else>
      <h1>Nav neviena vienuma.</h1>
      <h2>Pievienojiet jaunu darbu.</h2>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    works: Array,
    materials: Array,
    mechanisms: Array,
    properties: Object
  },
  created() {
    window.addEventListener("resize", this.windowResize);
  },
  data() {
    return {
      work: [{name: "", code: '', unit_of_measurement: '', amount: null, time_norm: null, pay_rate: null}],
      edited: {},
      checkedAll: false,
      checkedItems: [],
      materialMenuStyle: {
        display: 'none',
        left: '0px',
        top: '0px',
        transform: 'none'
      },
      mechanismMenuStyle: {
        display: 'none',
        left: '0px',
        top: '0px',
        transform: 'none'
      },
      dropdownStyle: {
        right: 0,
        transform: 'none'
      },
      itemForMaterial: {},
      menuMaterials: [],
      materialMenuSearch: '',
      itemForMechanism: {},
      menuMechanisms: [],
      mechanismMenuSearch: '',
      worksForExport: [],
      tabs: {
        import: {
          tabs: ['JSON', 'CSV'],
          active: 0
        }
      },
      search: [{ query: '', property: 'name' }],
      sort: {
        property: 'name',
        asc: true,
        disabled: true
      },
      errors: (function () {
        const errorNames = ['createWork', 'uploadJSON', 'uploadCSV'];
        const errors = {};
        errorNames.forEach(x => errors[x] = { message: '', display: false });
        return errors;
      })()
    };
  },
  computed: {
    worksWithSums() {
      const works = JSON.parse(JSON.stringify(this.works));
      for (const item of works) {
        item.salary = +(Math.ceil(item.time_norm * item.pay_rate + "e+2") + "e-2");
        item.sum = +(Math.ceil(item.salary * item.amount + "e+2") + "e-2");
      }
      return works;
    },
    filtered() {
      return this.worksWithSums
        .filter(x => this.search
        .every(field => field.query.split(',')
        .some(keyword => x[field.property].toString().toLowerCase()
        .includes(keyword.toLowerCase().trim()))))
        .sort((a, b) => {
          const { property, asc, disabled } = this.sort;
          if (disabled) return 0;
          if (asc) {
            if (a[property] < b[property]) return -1;
            else if (a[property] > b[property]) return 1;
            else return 0;
          }
          if (a[property] > b[property]) return -1;
          else if (a[property] < b[property]) return 1;
          else return 0;
        });
    },
    filteredMenuMaterials() {
      return this.menuMaterials.filter(x => x.name.toLowerCase().includes(this.materialMenuSearch.toString().toLowerCase()));
    },
    filteredMenuMechanisms() {
      return this.menuMechanisms.filter(x => x.name.toLowerCase().includes(this.mechanismMenuSearch.toString().toLowerCase()));
    }
  },
  methods: {
    addToEdited(item, key, value) {
      if (item[key] !== value && (key === 'code' || value.trim())) {
        if (!(item.id in this.edited)) this.edited[item.id] = {};
        this.edited[item.id][key] = value;
      } else {
        if (item.id in this.edited) {
          delete this.edited[item.id][key];
          if (Object.keys(this.edited[item.id]).length === 0) {
            delete this.edited[item.id];
          }
        }
      }
    },
    async createWork() {
      const items = this.work.filter(x => x.name !== '' && x.unit_of_measurement !== '' && x.amount && x.time_norm && x.pay_rate);
      if (this.work.length > items.length && items.length > 0) {
        this.graphqlMutation(items, 'create');
        const names = items.map(x => x.name);
        this.work = this.work.filter(x => !names.includes(x.name));
        this.displayError('createWork', `Pievienoti ${items.length} no ${this.work.length} vienumiem`);
      } else if (items.length === 0) {
        if (this.work.some(x => x.name !== "" || x.unit_of_measurement !== '' || x.amount || x.time_norm || x.pay_rate))
        this.displayError('createWork', `Vienums netika pievienots. Pārliecinieties, lai visi lauki būtu aizpildīti!`);
      } else {
        this.hideError('createWork');
        this.graphqlMutation(items, 'create');
        this.work = [{name: "", code: '', unit_of_measurement: '', amount: null, time_norm: null, pay_rate: null}];
      }
    },
    addWork() {
      this.work.push({name: "", code: '', unit_of_measurement: '', amount: null, time_norm: null, pay_rate: null,});
    },
    minusWork(index) {
      this.work.splice(index, 1);
    },
    async removeMaterial(item, materialId) {
      const ids = item.materials.map(x => x.id);
      const materials = ids.filter(x => x !== materialId);
      this.graphqlMutation([{id: item.id, materials: materials.length === 0 ? null : materials}], 'update');
    },
    async removeMechanism(item, mechanismId) {
      const ids = item.mechanisms.map(x => x.id);
      const mechanisms = ids.filter(x => x !== mechanismId);
      this.graphqlMutation([{id: item.id, mechanisms: mechanisms.length === 0 ? null : mechanisms}], 'update');
    },
    async updateWork() {
      const temp = [];
      for (const [key, value] of Object.entries(this.edited)) {
        temp.push({id: key, ...value})
      }
      const items = temp.filter(x => this.checkedItems.includes(x.id));
      if (items.length > 0) this.graphqlMutation(items, 'update');
      this.edited = {};
      this.checkedItems = [];
      this.checkedAll = false;
    },
    async deleteWork(ids) {
      this.graphqlMutation(ids, 'delete');
      this.checkedItems = [];
      this.checkedAll = false;
    },
    graphqlMutation(items, operation) {
      this.$emit('graphql-mutation', { items, operation, type: 'Work' });
    },
    showMaterialMenu(event, item) {
      if (item.materials === null) {
        this.menuMaterials = this.materials;
      } else {
        const ids = item.materials.map(x => x.id);
        this.menuMaterials = this.materials.filter(x => !ids.includes(x.id));
      }

      const { pageX: left, pageY: top } = event;
      const { materialMenuStyle: style } = this;
      const right = window.innerWidth - left;

      if (left >= 100 && right >= 100) { //menu is in the middle
        style.transform = 'translate(-50%, 0)';
      } else if (right < 100) { //menu is on the right side
        style.transform = 'translate(-100%, 0)';
      } else { //menu is on the left side
        style.transform = 'none';
      }

      style.left = left + 'px';
      style.top = top + 'px';
      style.display = 'block';
      this.materialMenuStyle = style;
      this.itemForMaterial = item;

      this.$nextTick(() => {
        this.$refs.materialMenuSearch.focus();
      });
    },
    async updateWithMaterial(id) {
      const item = this.itemForMaterial;
      let ids = [];
      if (item.materials) item.materials.forEach(x => ids.push(x.id));
      this.graphqlMutation([{id: item.id, materials: [...ids, id]}], 'update');
      this.materialMenuSearch = '';
      this.$refs.materialMenu.blur();
    },
    showMechanismMenu(event, item) {
      if (item.mechanisms === null) {
        this.menuMechanisms = this.mechanisms;
      } else {
        const ids = item.mechanisms.map(x => x.id);
        this.menuMechanisms = this.mechanisms.filter(x => !ids.includes(x.id));
      }

      const { pageX: left, pageY: top } = event;
      const { mechanismMenuStyle: style } = this;
      const right = window.innerWidth - left;

      if (left >= 100 && right >= 100) { //menu is in the middle
        style.transform = 'translate(-50%, 0)';
      } else if (right < 100) { //menu is on the right side
        style.transform = 'translate(-100%, 0)';
      } else { //menu is on the left side
        style.transform = 'none';
      }

      style.left = left + 'px';
      style.top = top + 'px';
      style.display = 'block';
      this.mechanismMenuStyle = style;
      this.itemForMechanism = item;

      this.$nextTick(() => {
        this.$refs.mechanismMenuSearch.focus();
      });
    },
    async updateWithMechanism(id) {
      const item = this.itemForMechanism;
      let ids = [];
      if (item.mechanisms) item.mechanisms.forEach(x => ids.push(x.id));
      this.graphqlMutation([{id: item.id, mechanisms: [...ids, id]}], 'update');
      this.mechanismMenuSearch = '';
      this.$refs.mechanismMenu.blur();
    },
    copy(text) {
      navigator.clipboard.writeText(text).catch(error => {
        console.error(error);
      });
    },
    generateForExport() {
      this.worksForExport = this.works.map(x => {
        if (x.materials === null && x.mechanisms === null) return x;
        const work = JSON.parse(JSON.stringify(x));
        if (work.materials && work.materials.length > 0) work.materials = work.materials.map(y => y.id);
        if (work.mechanisms && work.mechanisms.length > 0) work.mechanisms = work.mechanisms.map(y => y.id);
        return work;
      });
    },
    worksToCSV() {
      const { worksForExport } = this;
      const header = Object.keys(worksForExport[0]);
      const csv = [
        header.join(';'),
        ...worksForExport.map(row => header.map(fieldName => {
          return row[fieldName];
        }).join(';'))
      ].join('\r\n');
      return csv;
    },
    uploadJSON() {
      const fileReader = new FileReader();
      const graphqlMutation = (input, operation) => this.graphqlMutation(input, operation);
      fileReader.onload = function(event) {
        const result = JSON.parse(event.target.result);
        const allowedFields = ['id', 'name', 'code', 'unit_of_measurement', 'amount', 'time_norm', 'pay_rate', 'materials', 'mechanisms'];
        result.forEach(row => {
          for (const key of Object.keys(row)) {
            if (!allowedFields.includes(key)) delete row[key];
          }
        });
        graphqlMutation(result, 'create', 'Work');
      };
      if (this.$refs.uploadJSON.files.length > 0) {
        this.hideError('uploadJSON');
        fileReader.readAsText(this.$refs.uploadJSON.files[0]);
      }
      else this.displayError('uploadJSON', 'Pievienojiet failu');
    },
    uploadCSV() {
      const fileReader = new FileReader();
      const graphqlMutation = (input, operation) => this.graphqlMutation(input, operation);
      fileReader.onload = function(event) {
        const result = event.target.result;
        const rows = result.split('\r\n');
        const header = rows[0].split(';');
        rows.splice(0, 1);
        const resultArr = [];
        rows.forEach(row => {
          const splitRow = row.split(';');
          const rowObj = {};
          for (let i = 0; i < splitRow.length; i++) {
            rowObj[header[i]] = splitRow[i];
          }
          rowObj.materials === '' ? rowObj.materials = null : rowObj.materials = rowObj.materials.split(',');
          rowObj.mechanisms === '' ? rowObj.mechanisms = null : rowObj.mechanisms = rowObj.mechanisms.split(',');
          resultArr.push(rowObj);
        });
        const allowedFields = ['id', 'name', 'code', 'unit_of_measurement', 'amount', 'time_norm', 'pay_rate', 'materials', 'mechanisms'];
        resultArr.forEach(row => {
          for (const [key, value] of Object.entries(row)) {
            if (!allowedFields.includes(key)) delete row[key];
            if (value === '') row[key] = null;
          }
        });
        graphqlMutation(resultArr, 'create', 'Work');
      };
      if (this.$refs.uploadCSV.files.length > 0) {
        this.hideError('uploadCSV');
        fileReader.readAsText(this.$refs.uploadCSV.files[0]);
      }
      else this.displayError('uploadCSV', 'Pievienojiet failu');
    },
    addQuery() {
      this.search.push({ query: '', property: 'name' });
    },
    removeQuery(index) {
      this.search.splice(index, 1);
    },
    changeSort(property) {
      if (this.sort.property === property && !this.sort.asc && !this.sort.disabled) {
        this.sort.disabled = true;
      } else if (this.sort.disabled) {
        this.sort.property = property;
        this.sort.asc = true;
        this.sort.disabled = false;
      } else {
        if (this.sort.property === property) this.sort.asc = !this.sort.asc;
        else this.sort.property = property;
      }
    },
    displayError(errorName, message) {
      for (const key of Object.keys(this.errors)) {
        this.errors[key].display = false;
      }
      this.errors[errorName].message = message;
      this.errors[errorName].display = true;
    },
    hideError(errorName) {
      if (this.errors[errorName].display) this.errors[errorName].display = false;
    },
    openImportDropdown(target) {
      this.$set(this.tabs.import.tabs, 'active', this.tabs.import.tabs.active || 0);

      const rect = target.getBoundingClientRect();
      const left = window.scrollX + rect.left; //pogas attālums no kreisās puses
      const right = window.scrollX + (window.innerWidth - rect.right); //pogas attālums no labās puses
      const { dropdownStyle: style } = this;

      if (left + rect.width - 200 <= 0 && right + rect.width - 200 <= 0) { //izvēlne atrodas vidū
        style.right = '50%';
        style.transform = 'translate(50%, 0)';
      } else if (left + rect.width - 200 <= 0) { //izvēlne atrodas labajā pusē
        style.transform = 'none';
        style.right = 'unset';
      } else if (right + rect.width - 200 <= 0) { //izvēlne atrodas kreisajā pusē
        style.transform = 'none';
        style.right = '0';
      } else { 
        style.transform = 'none';
        style.right = '0';
      }

      this.dropdownStyle = style;
    },
    windowResize() {
      if (this.materialMenuStyle.display === 'block') this.materialMenuStyle.display = 'none';
      if (this.mechanismMenuStyle.display === 'block') this.mechanismMenuStyle.display = 'none';
    }
  },
  destroyed() {
    window.removeEventListener("resize", this.windowResize);
  }
}
</script>