<template>
  <q-card class="q-ma-sm">
    <q-card-section>
      <div>{{ $t('debugCode.label') }}</div>
    </q-card-section>
    <q-card-section class="row items-start q-gutter-xs no-wrap">
      <div class="col-7 DebugCode-CodeBlock">
        <codemirror
          class="DebugCode-CodeMirror"
          ref="myCm"
          :value="code"
          :options="cmOptions"
          @ready="onCmReady"
          @input="onCmCodeChange"
        />
        <q-btn
          class="DebugCode-RunButton"
          label="Выполнить"
          color="primary"
          outline
          @click="runScript"
        />
        <q-btn
          class="DebugCode-FullScreenButton"
          :class="{ 'DebugCode-FullScreenButton_Full':fullScreenFlag,
          'DebugCode-FullScreenButton_Not':!fullScreenFlag }"
          :icon="fullScreenIcon"
          @click="codeMirroFullScreen"
        />
      </div>
      <div class="row col-5">
        <q-input
          class="col-12"
          v-model="idApplication"
          label="ID заявления"
          @blur="afterInsertId"
          :disable="applicationLoadFlag"
          outlined
        />
        <p class="col-6 flex items-center">
          Переменная application
          <q-skeleton v-if="applicationLoadFlag" type="rect"/>
          <q-icon
            v-else
            size="sm"
            :name="!statusApplicationLoad ? 'close' : 'check'"
          />
        </p>
        <p class="col-6 flex items-center">
          Переменная service
          <q-skeleton v-if="applicationLoadFlag" type="rect"/>
          <q-icon
            v-else
            size="sm"
            :name="!statusServiceLoad ? 'close' : 'check'"
          />
        </p>
        <div class="row no-wrap col-12 q-mt-sm items-start">
          <q-input
            class="col-3 q-pr-xs"
            style="margin-bottom: 20px"
            v-model="variableName"
            label="Имя переменной"
            outlined
          />
          <q-select
            class="col-2 q-pr-xs"
            label="Тип"
            style="margin-bottom: 20px"
            v-model="variableType"
            :options="types"
            outlined
          />
          <q-input
            v-if="variableType === ''"
            class="col-7"
            style="margin-bottom: 20px"
            label="Значение"
            v-model="variableValue"
            :type="variableType"
            outlined
            :disable="!variableType"

          />
          <q-input
            v-if="variableType.value === 'string' ||
            variableType.value === 'number' ||
            variableType.value === 'date'"
            class="col-7"
            label="Значение"
            v-model="variableValue"
            :type="variableType.value"
            outlined
            :disable="!variableType"
          />
          <q-input
            v-if="variableType.value === 'boolean'"
            class="col-7"
            label="Значение"
            v-model="variableValue"
            outlined
            :disable="!variableType"
          />
          <v-jsoneditor
            v-if="variableType.value === 'json' || variableType.value === 'array'"
            v-model="variableValue"
            :options="{ mode: 'code' }"
          />
        </div>
        <div class="row col-12">
          <q-btn
            class="q-mt-sm"
            outline
            label="Добавить"
            @click="addVariable"
          />
        </div>
        <div class="DebugCode-Row q-mt-xs">
            <span>Имя переменной</span>
            <span>Тип переменной</span>
            <span>Значение переменной</span>
            <span></span>
        </div>
        <div class="DebugCode-Row" v-for="variable in variables" :key="variable.name">
          <span>{{ variable.variableName }}</span>
          <span>{{ variable.variableType }}</span>
          <span v-if="variable.variableType === 'String' || variable.variableType === 'Number'">{{ variable.variableValue }}</span>
          <v-jsoneditor
            v-if="variable.variableType === 'JSON' || variable.variableType === 'Array'"
            v-model="variable.variableValue"
            :options="{
              name: variable.variableName,
              mode: 'code',
              onEditable({}){},
              mainMenuBar: false,
            }"
          />
          <span>
            <q-icon
              class="DebugCode-DeleteButton"
              name="close"
              @click="deleteVariable(variable.variableName)"
            />
          </span>
        </div>
      </div>
    </q-card-section>
    <q-card-section class="row no-wrap q-gutter-xs">
      <q-skeleton v-if="runScriptLoader" type="text"/>
      <div v-else class="col-6">
        <v-jsoneditor
          style="height: 400px"
          v-if="responseType === 'object'"
          v-model="scriptResult"
          :options="{ mode: 'code', onEditable({}){} }"
        />
        <div class="DebugCode-Result q-pa-xs" v-else>
          {{ scriptResult }}
        </div>
      </div>
      <q-skeleton v-if="runScriptLoader" type="text"/>
      <div
        class="col-6 DebugCode-Result q-pa-xs"
        v-else
        :class="{ 'DebugCode-Result_Error': scriptResultErrorFlag }"
      >
        {{ scriptConsole }}
      </div>
    </q-card-section>
    <q-dialog v-model="apiError">
      <q-card>
        <q-card-section>
          <sapn class="text-h5">Ошибка</sapn>
        </q-card-section>
        <q-card-section>
          <pre>
            {{ textApiError }}
          </pre>
        </q-card-section>
      </q-card>
    </q-dialog>
  </q-card>
</template>

<script>
import { codemirror } from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/groovy/groovy'
import 'codemirror/theme/ayu-dark.css'
import 'codemirror/addon/display/fullscreen'
import 'codemirror/addon/display/fullscreen.css'
import VJsoneditor from 'v-jsoneditor'
import { DocumentApi } from '@openapi/document-registry'
import { ServiceApi } from '@openapi/registry-service'
import { ScriptVariableDTOVariableTypeEnum, WorkflowServiceApi } from '@openapi/wf'
import { notifyError } from '@utils/notify'

export default {
  name: 'debug-code',
  components: {
    codemirror,
    VJsoneditor
  },
  data() {
    return {
      code: '',
      cmOptions: {
        tabSize: 2,
        mode: 'text/x-groovy',
        // theme: 'ayu-dark',
        lineNumbers: true,
        line: true,
        readOnly: false
      },
      runScriptLoader: false,
      idApplication: '',
      applicationLoadFlag: false,
      types: Object.values(ScriptVariableDTOVariableTypeEnum).map(e =>
        {
          return {
            label: e,
            value: e.toLowerCase()
          }
        }
      ),
      statusApplicationLoad: false,
      statusServiceLoad: false,
      variableName: '',
      variableType: '',
      variableValue: '',
      variables: [],
      scriptResultErrorFlag: false,
      scriptResult: 'Результат выполнения скрипта',
      scriptConsole: 'Вывод логов',
      sendingData: [
        {
          variableName: 'application',
          variableType: ScriptVariableDTOVariableTypeEnum.Json,
          variableValue: {}
        },
        {
          variableName: 'service',
          variableType: ScriptVariableDTOVariableTypeEnum.Json,
          variableValue: {}
        }
      ],
      apiError: false,
      textApiError: '',
      fullScreenFlag: false,
      fullScreenIcon: 'open_in_full',
      responseType: 'text'
    }
  },
  methods: {
    onCmReady(cm) {
      console.log('the editor is readied!', cm)
    },
    onCmCodeChange(newCode) {
      this.code = newCode
    },
    async afterInsertId () {
      this.applicationLoadFlag = true
      if (this.idApplication) {
        try {
          const documentApi = new DocumentApi()
          const serviceApi = new ServiceApi()
          const application = await documentApi.getDocument(
            'Applications',
            this.idApplication,
            ['applicant','delegate','history','documents','interDepartmentReq','payments','relatives'],
          )
          this.sendingData.forEach(e => {
            if (e.variableName === 'application') {
              e.variableValue = application.data
            }
          })
          const service = await serviceApi.getService(
            application.data.system.serviceId,
            ['tables','processes','documents.form','model','form','interagencies',
              'dictionaries','reglament','paymentLinks','formsExtra','interagencies.formRequest',
              'interagencies.formResponse']
          )
          this.sendingData.forEach(e => {
            if (e.variableName === 'service') {
              e.variableValue = service.data
            }
          })

          this.statusApplicationLoad = true
          this.statusServiceLoad = true
        } catch (e) {
          if (e.response.status === 404) {
            notifyError(e.response.data.message)
          } else {
            notifyError('Не удалось получить данные по заявлению')
          }
          this.idApplication = ''
          this.statusApplicationLoad = false
          this.statusServiceLoad = false
        }
      }
      this.applicationLoadFlag = false
    },
    addVariable () {
      if (this.variables.find(e => e.variableName === this.variableName)) {
        notifyError('Переменная с таким именем уже существует')
        return
      }
      if (!this.variableName) {
        notifyError('Не указано имя переменной')
        return
      }
      if (this.variableName.match(/^\d/)) {
        notifyError('Имя переменной не может начинаться с цифры')
        return
      }
      if (this.variableName.match(/[а-яА-Я]/g)) {
        notifyError('в имени переменной допускаются только латинские буквы')
        return
      }
      if (!this.variableType) {
        notifyError('Не указан тип переменной')
        return
      }
      if (!this.variableValue) {
        notifyError('Нет начального значения переменной')
        return
      }
      try {
        console.log(this.variableValue)
        switch (this.variableType.label) {
          case 'JSON':
          case 'Array':
            JSON.parse(JSON.stringify(this.variableValue))
            break
        }
        this.variables.push(
          {
            variableName: this.variableName,
            variableType: this.variableType.label,
            variableValue: this.variableValue
          }
        )
        this.variableValue = ''
        this.variableName = ''
        this.variableType = ''
      } catch (e) {
        notifyError('Не валидное значение переменной')
      }
    },
    deleteVariable(name) {
      this.variables = this.variables.filter(e => e.variableName !== name)
    },
    runScript () {
      if (!this.code) {
        notifyError('У вас нет скрипта')
        return
      }
      this.runScriptLoader = true
      this.cmOptions.readOnly = true
      this.scriptResultErrorFlag = false
      this.scriptConsole = ''
      this.scriptResult = ''
      this.variables.forEach(e => {
        switch (e.variableType) {
          case 'Number':
            e.variableValue = Number(e.variableValue)
            break
          case 'JSON':
          case 'Array':
            if (typeof e.variableValue !== 'object') {
              e.variableValue = JSON.parse(e.variableValue)
            }
            break
        }
      })
      const wfApi = new WorkflowServiceApi()
      wfApi.evaluateGroovyScript({
        script: this.code,
        parameters: [...this.sendingData, ...this.variables]
      })
        .then(response => {
          if (!response.data.success) {
            notifyError('Запрос прошел успешно, но в результате выполнения скрипта, ' +
              'произошла ошибка!')
            this.scriptResultErrorFlag = true
          }
          console.log(typeof response.data.result)
          if (typeof response.data?.result === 'object') {
            this.responseType = 'object'
          } else {
            this.responseType = 'text'
          }
          this.scriptResult = response.data?.result || 'Скрипт не вернул результатов'
          this.scriptConsole = response.data?.stdOut
        }).catch(e => {
        console.log(e.response)
        this.apiError = true
        switch (e.response.status) {
          case 408:
          case 400:
          case 500:
            notifyError(`Произошла ошибка ${e.response.statusText}
            Попробуйте позже`)
            this.textApiError = e.response.data.trace
            break
          default:
            notifyError('Во время выполнения скрипта произошла ошибка')
        }
      })
        .finally(() => {
          this.runScriptLoader = false
          this.cmOptions.readOnly = false
        })
    },
    codeMirroFullScreen () {
      this.codemirror.setOption('fullScreen', !this.codemirror.getOption('fullScreen'))
      this.fullScreenFlag = this.codemirror.getOption('fullScreen')
      this.fullScreenIcon = this.fullScreenFlag ? 'close_fullscreen' : 'open_in_full'
    }
  },
  watch: {
    apiError: () => {
      this.textApiError = ''
    },
    variableType: {
      handler(newVal) {
        if (newVal.value === 'json') {
          this.variableValue = {}
        } else if (newVal.value === 'array') {
          this.variableValue = []
        } else {
          this.variableValue = ''
        }
      },
      deep: true
    }
  },
  computed: {
    codemirror() {
      return this.$refs.myCm.codemirror
    }
  }
}
</script>

<style lang="scss">
.CodeMirror {
  height: 700px;
  &-fullscreen {
    z-index: 9000;
  }
}
.DebugCode {
  &-CodeBlock {
    position: relative;
  }
  &-RunButton {
    position: absolute;
    top: -35px;
    right: 0;
    z-index: 1000;
  }
  &-FullScreenButton{
    &_Not {
      position: absolute;
      top: 0;
      right: 0;
      z-index: 1000;
    }
    &_Full {
      position: fixed;
      top: 0;
      right: 0;
      z-index: 9100;
    }
  }
  &-CodeMirror {
    border: 1px solid grey;
  }
  &-Row {
    width: 100%;
    display: grid;
    grid-template-columns: 100px 100px 2fr 50px;
    & > span {
      padding: 4px;
      display: flex;
      align-items: center;
      border: 1px solid darkgrey;
      &:not(:last-child) {
        border-right: none;
      }
    }
  }
  &-DeleteButton {
    cursor: pointer;
  }
  &-Result {
    border: 1px solid grey;
    overflow-wrap: break-word;
    &_Error {
      background: pink;
    }
  }
}
</style>
