<script setup lang="ts">
import { PropType, ref, watch } from 'vue'
import { copy } from 'copy-anything'
import { getProp } from 'path-to-prop'
import { isArray, isFunction, isObject } from 'is-what'
import CButton from '../CButton/CButton.vue'
import CTrash from '../CTrash/CTrash.vue'
import { FieldProps, Validation, ErrorsPerField } from './CFormWrapper.types'

const props = defineProps({
  modelValue: { type: Object as PropType<Record<string, any>>, required: true },
  validation: { type: Object as PropType<Validation>, default: undefined },
  // 削除権限
  p_DeleteAuth: { type: Boolean, default: false },
  mode: { type: String as PropType<'edit' | 'add'>, required: true },
  // 閲覧モード
  p_DisableView: { type: Boolean, default: false },
  // 「編集する」から開始するモード
  p_startEditable: { type: Boolean, default: false },
  // 「登録確認」のボタンラベル
  p_ButtonLabel: { type: String, default: '登録確認' },
  // 編集モードのv-model
  editFlag: { type: Boolean, default: undefined },
})

const emit = defineEmits<{
  (e: 'edit'): void
  (e: 'save'): void
  (e: 'cancel'): void
  (e: 'close'): void
  (e: 'delete'): void
  (e: 'update:modelValue', payload: Record<string, any>): void
  (e: 'update:editFlag', payload: boolean): void
}>()

const fieldProps = ref<FieldProps>({
  disabled: undefined,
})

let modelValueBackup = copy(props.modelValue)

watch(
  () => props.modelValue,
  (newValue) => {
    if (newValue.id && !modelValueBackup.id) {
      modelValueBackup = copy(newValue)
    }
  },
  { deep: true }
)
const errorText = ref<null | string>(null)
const errorsPerField = ref<ErrorsPerField>({})
function checkForErrors(): null | string {
  if (!props.validation) return null

  // errorsをクリアする
  errorsPerField.value = {}

  for (const [id, validateInfo] of Object.entries(props.validation)) {
    const val = getProp(props.modelValue, id)
    if (validateInfo.required) {
      if (!val) {
        const error = '必須フィールド'
        errorsPerField.value[id] = error
        return error
      } else {
        errorsPerField.value[id] = null
      }
    }
    if (isFunction(validateInfo.error)) {
      const error = validateInfo.error(val)

      if (error) {
        // const foundError = isArray(error) ? error.find(Boolean) : error

        let getError: string = ''

        if (isArray(error)) {
          error.map((d) => {
            if (isObject(d)) {
              Object.values(d).map((o) => {
                if (o) {
                  errorsPerField.value[id] = error
                  getError = !!getError ? getError : o
                }
              })
            } else {
              errorsPerField.value[id] = error
              getError = !!getError ? getError : (d as string)
            }
          })

          if (getError) return getError
        } else if (error) {
          errorsPerField.value[id] = error
          return error
          // return foundError
        }
      } else {
        errorsPerField.value[id] = null
      }
    }
  }
  return null
}
function resetErrors() {
  errorText.value = null
  Object.keys(errorsPerField.value).forEach((id) => (errorsPerField.value[id] = null))
}

const formEditable = ref(props.mode === 'add')

watch(
  formEditable,
  (yes) => {
    if (yes) {
      fieldProps.value.disabled = undefined
    } else {
      fieldProps.value.disabled = true
      resetErrors()
    }
    emit('update:editFlag', yes)
  },
  { immediate: true }
)
// editflagのbackup
const beforeEditFlag = ref(props.editFlag)
// editFlagが変更されたときにformEdtableを変更する
watch(
  () => props.editFlag,
  () => {
    if (beforeEditFlag.value === props.editFlag) return
    if (props.editFlag === undefined || props.mode === 'add') return
    beforeEditFlag.value = props.editFlag
    formEditable.value = props.editFlag
  }
)

function save() {
  const error = checkForErrors()
  if (error) {
    errorText.value = error
    return
  }
  errorText.value = null
  // 編集状態を終了させる　⇒　編集状態を維持する
  formEditable.value = true
  emit('save')
}

function edit() {
  formEditable.value = true
  emit('edit')
}

function cancel() {
  emit('update:modelValue', copy(modelValueBackup))
  emit('cancel')

  if (props.mode === 'edit') {
    formEditable.value = false
  }
}
function close() {
  emit('close')
  formEditable.value = false
}
function clickDelete() {
  emit('delete')
}
// const docId = 'test'
</script>

<template>
  <div class="_c_form_wrapper">
    <!-- <div class="_top">
      <CToggle v-model="formEditable" p_Title="編集" />
    </div> -->

    <slot v-bind="{ fieldProps, errorsPerField }" />
    <div class="_error_space">
      <div v-if="errorText" class="_error">{{ errorText }}</div>
    </div>
    <div class="_bottom">
      <CButton
        v-if="!formEditable && !p_DisableView && !p_startEditable"
        p_Label="編集する"
        :disabled="formEditable"
        @click="edit"
        class="_button"
      />
      <CButton
        v-if="!formEditable && !p_DisableView && p_startEditable"
        p_Label="編集する"
        :disabled="formEditable"
        @click="edit"
        class="_button"
      />
      <CButton
        v-if="(!formEditable && !p_startEditable) || p_DisableView"
        p_Label="閉じる"
        :disabled="formEditable"
        p_BtnType="secondary"
        @click="close"
        class="_button"
      />
      <CButton
        v-if="formEditable"
        :p_Label="p_ButtonLabel"
        :disabled="!formEditable"
        @click="save"
        class="_button"
      />
      <CButton
        v-if="formEditable"
        p_Label="キャンセル"
        :disabled="!formEditable"
        p_BtnType="secondary"
        @click="cancel"
        class="_button"
      />
      <CTrash v-if="formEditable && p_DeleteAuth" class="_deleteBtn" @click="clickDelete" />
    </div>
  </div>
</template>

<style scoped lang="sass">
._top
  padding-bottom: 1rem
._error_space
  height: 40px
._error
  padding-top: 1rem
  color: red
._bottom
  display: flex
  justify-content: center
  align-items: center
  padding-top: 1rem
  display: flex
  margin-left: -1rem
  margin-top: -1rem
  position: relative
  > *
    margin-left: 1rem
    margin-top: 1rem

.vfm__content ._bottom ._button
  width: 150px
  margin-left: 20px
._button
  width: 180px
  margin-left: 20px

._deleteBtn
  position: absolute
  bottom: 0
  right: 0
</style>
