<template>
  <div class="field-renderer">
    <section
      v-for="group in fieldGroups"
      :key="group.area"
      class="field-area"
    >
      <h3>{{ group.area }}</h3>

      <div class="field-grid">
        <label
          v-for="field in group.fields"
          :key="field.field_path"
          class="field-item"
          :class="{ 'field-item--wide': isTableField(field) || isFileField(field) }"
        >
          <span class="field-label">
            {{ field.display_name }}
            <sup v-if="isReservationFieldRequired(field)">*</sup>
          </span>

          <select
            v-if="isEditable(field) && isSelectField(field)"
            class="field-control"
            :aria-invalid="fieldError(field) ? 'true' : 'false'"
            :value="fieldValue(field)"
            @change="updateField(field, $event)"
          >
            <option
              v-for="option in parseEnumOptions(field.enum_options)"
              :key="option.value"
              :value="option.value"
            >
              {{ option.label }}
            </option>
          </select>

          <input
            v-else-if="isEditable(field) && isDateField(field)"
            class="field-control"
            type="date"
            :aria-invalid="fieldError(field) ? 'true' : 'false'"
            :value="fieldValue(field)"
            @input="updateField(field, $event)"
          >

          <input
            v-else-if="isEditable(field) && isNumberField(field)"
            class="field-control"
            type="number"
            :aria-invalid="fieldError(field) ? 'true' : 'false'"
            :value="fieldValue(field)"
            @input="updateField(field, $event)"
          >

          <textarea
            v-else-if="isEditable(field)"
            class="field-control field-control--textarea"
            :aria-invalid="fieldError(field) ? 'true' : 'false'"
            :value="fieldValue(field)"
            rows="3"
            @input="updateField(field, $event)"
          />

          <span
            v-else-if="isFileField(field)"
            class="field-static field-static--file"
          >
            <i
              class="pi pi-paperclip"
              aria-hidden="true"
            />
            {{ stringifyReservationValue(fieldValue(field), t('task.emptyValue')) }}
          </span>

          <span
            v-else-if="isTableField(field)"
            class="field-static field-static--table"
          >
            {{ stringifyReservationValue(fieldValue(field), t('task.emptyValue')) }}
          </span>

          <span
            v-else
            class="field-static"
          >
            {{ stringifyReservationValue(fieldValue(field), t('task.emptyValue')) }}
          </span>

          <small
            v-if="field.notes"
            class="field-note"
          >{{ field.notes }}</small>
          <small
            v-if="fieldError(field)"
            class="field-error"
          >{{ fieldError(field) }}</small>
        </label>
      </div>
    </section>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

import type { ReservationRecord, ReservationTaskFieldResult } from '@/types/reservation'
import {
  groupReservationFields,
  isDateField,
  isFileField,
  isNumberField,
  isReservationFieldEditable,
  isReservationFieldRequired,
  isSelectField,
  isTableField,
  readReservationFieldValue,
  parseEnumOptions,
  stringifyReservationValue,
} from '@/utils/reservationFieldRules'

const props = defineProps<{
  fields: ReservationTaskFieldResult[]
  modelValue: ReservationRecord
  readOnly: boolean
  validationErrors?: Record<string, string>
}>()

const emit = defineEmits<{
  'update:modelValue': [value: ReservationRecord]
}>()

const { t } = useI18n()

const fieldGroups = computed(() => groupReservationFields(props.fields, t('task.fieldAreaFallback'), props.modelValue))

function fieldValue(field: ReservationTaskFieldResult): string | number | readonly string[] {
  const value = readReservationFieldValue(field, props.modelValue)
  if (Array.isArray(value)) {
    return value.map((item) => stringifyReservationValue(item, t('task.emptyValue')))
  }
  if (typeof value === 'number') {
    return value
  }
  if (typeof value === 'string') {
    return value
  }
  return stringifyReservationValue(value, t('task.emptyValue'))
}

function isEditable(field: ReservationTaskFieldResult): boolean {
  return isReservationFieldEditable(field, props.readOnly)
}

function fieldError(field: ReservationTaskFieldResult): string {
  return props.validationErrors?.[field.field_path] ?? ''
}

function updateField(field: ReservationTaskFieldResult, event: Event): void {
  const target = event.target as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
  emit('update:modelValue', {
    ...props.modelValue,
    [field.field_path]: target.value,
  })
}
</script>

<style scoped>
.field-renderer {
  display: grid;
  gap: 18px;
}

.field-area {
  display: grid;
  gap: 12px;
}

.field-area h3 {
  margin: 0;
  color: var(--th-color-slate-900);
  font-size: 14px;
  font-weight: 800;
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.field-item {
  display: grid;
  min-width: 0;
  gap: 7px;
}

.field-item--wide {
  grid-column: 1 / -1;
}

.field-label {
  color: var(--th-color-slate-500);
  font-size: 12px;
  font-weight: 700;
}

.field-label sup {
  color: var(--th-color-danger);
}

.field-control,
.field-static {
  min-height: 38px;
  width: 100%;
  min-width: 0;
  border: 1px solid var(--th-color-slate-200);
  border-radius: var(--th-radius-sm);
  background: var(--th-color-white);
  color: var(--th-color-slate-900);
  font-size: 13px;
  line-height: 1.5;
}

.field-control {
  padding: 8px 10px;
}

.field-control--textarea {
  resize: vertical;
}

.field-static {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-wrap: anywhere;
  padding: 8px 10px;
  background: var(--th-color-slate-50);
}

.field-static--file {
  color: var(--th-color-info);
}

.field-static--table {
  align-items: flex-start;
  white-space: normal;
}

.field-note {
  color: var(--th-color-slate-500);
  font-size: 12px;
  line-height: 1.55;
}

.field-error {
  color: var(--th-color-danger);
  font-size: 12px;
  font-weight: 800;
  line-height: 1.55;
}

.field-control[aria-invalid='true'] {
  border-color: var(--th-color-danger);
  box-shadow: 0 0 0 1px var(--th-color-danger);
}

@media (max-width: 760px) {
  .field-grid {
    grid-template-columns: 1fr;
  }
}
</style>
