<script lang="ts">
import {
  defineComponent, ref, shallowRef, computed, watch, toRaw, type PropType,
} from 'vue';
import { cmp, Date, DateTime, Time, timedelta } from '@silane/datetime';

import InputCourseDate from './InputCourseDate.vue';
import InputTimeLabel from './InputTimeLabel.vue';
import { inject, keys as injectionKeys } from '../injection';
import { datetimeRange } from '../common/utils.js';
import type { TimeLabel } from '../composables/timelabelstore';


export default defineComponent({
  name: 'InputEventTime',
  components: { InputCourseDate, InputTimeLabel },
  props: {
    modelValue: { type: Object as PropType<{
      startTime: DateTime, endTime: DateTime, timeLabel: TimeLabel | null,
    }>, required: true },
    invalid: { type: Boolean, required: true },
  },
  emits: ['update:modelValue', 'update:invalid'],
  setup(props, { emit }) {
    const dateRange = inject(injectionKeys.generalTimetableScope).dateRange;
    const courseDates = computed(() => [...datetimeRange(
      dateRange.value[0], dateRange.value[1], timedelta({ days: 1 })
    )]);
    const courseDatesISOFormat = computed(
      () => courseDates.value.map(x => x.isoFormat())
    );

    const overnight = ref(false);
    const startDateTime = shallowRef<DateTime | null>(
      DateTime.combine(courseDates.value[0], new Time())
    );
    const endDateTime = shallowRef<DateTime | null>(
      DateTime.combine(courseDates.value[0], new Time())
    );
    const date = shallowRef<Date | null>(courseDates.value[0]);
    const timeLabel = shallowRef<TimeLabel | null>(null);
    const startTime = shallowRef<Time | null>(new Time());
    const endTime = shallowRef<Time | null>(new Time());

    const invalidity = computed(() => {
      const errors: string[] = [];
      if(overnight.value) {
        if(!startDateTime.value) errors.push('startdatetime-null');
        if(!endDateTime.value) errors.push('enddatetime-null');
        if(startDateTime.value && endDateTime.value && cmp(
          startDateTime.value, endDateTime.value
        ) >= 0) errors.push('datetime-negative');
      } else {
        if(!date.value) errors.push('date-null');
        if(!timeLabel.value) {
          if(!startTime.value) errors.push('starttime-null');
          if(!endTime.value) errors.push('endtime-null');
          if(startTime.value && endTime.value && cmp(
            startTime.value, endTime.value
          ) >= 0) errors.push('time-negative');
        }
      }
      return errors;
    });

    const modelValue = computed(() => {
      if(invalidity.value.length) return null;
      if(overnight.value) {
        return {
          startTime: startDateTime.value!, endTime: endDateTime.value!,
          timeLabel: null,
        };
      } else {
        const theDate = date.value!;
        if(timeLabel.value) {
          return {
            startTime: DateTime.combine(theDate, timeLabel.value.startTime),
            endTime: DateTime.combine(theDate, timeLabel.value.endTime),
            timeLabel: timeLabel.value,
          };
        } else {
          return {
            startTime: DateTime.combine(theDate, startTime.value!),
            endTime: DateTime.combine(theDate, endTime.value!),
            timeLabel: null,
          };
        }
      }
    });

    let lastEmittedModelValue: typeof props.modelValue | null = null;
    watch(() => props.modelValue, () => {
      function setDate(x: Date) {
        const currentDate = date.value;
        if(currentDate && cmp(x, currentDate) === 0) return;
        date.value = x;
      }

      if(toRaw(props.modelValue) === lastEmittedModelValue) return;
      startDateTime.value = props.modelValue.startTime;
      endDateTime.value = props.modelValue.endTime;
      setDate(props.modelValue.startTime.date());
      timeLabel.value = props.modelValue.timeLabel;
      overnight.value = cmp(
        props.modelValue.startTime.date(), props.modelValue.endTime.date()
      ) !== 0;
      if(!overnight.value) {
        startTime.value = props.modelValue.startTime.time();
        endTime.value = props.modelValue.endTime.time();
      }
    }, { immediate: true });

    watch(invalidity, () => {
      emit('update:invalid', invalidity.value.length > 0);
    }, { immediate: true });

    watch(modelValue, () => {
      if(!modelValue.value) return;
      lastEmittedModelValue = modelValue.value;
      emit('update:modelValue', modelValue.value);
    });

    return {
      overnight, startDateTime, endDateTime, date, timeLabel,
      startTime, endTime, invalidity,
    }
  },
});
</script>

<template>
  <template v-if="overnight">
    <label>開始日時: <InputDateTime v-model="startDateTime" /></label>
    <label>終了日時: <InputDateTime v-model="endDateTime" /></label>
  </template>
  <template v-else>
    <label>
      日付:
      <InputCourseDate v-model="date" no-selection-text="任意の日付" />
    </label>
    <label>
      時間帯:
      <InputTimeLabel v-model="timeLabel" no-selection-text="任意の時刻" />
    </label>
    <template v-if="!timeLabel">
      <label>開始時刻: <InputTime v-model="startTime" /></label>
      <label>終了時刻: <InputTime v-model="endTime" /></label>
    </template>
  </template>
  <label><input type="checkbox" v-model="overnight" />日をまたぐ</label>
  <ul>
    <li v-for="error of invalidity" :key="error">
      {{ {
        'startdatetime-null': '開始日時が指定されていません',
        'enddatetime-null': '終了日時が指定されていません',
        'datetime-negative': '開始日時が終了日時より後か同じ日時です',
        'date-null': '日付が指定されていません',
        'starttime-null': '開始時刻が指定されていません',
        'endtime-null': '終了時刻が指定されていません',
        'time-negative': '開始時刻が終了時刻より後か同じ時刻です',
      }[error] }}
    </li>
  </ul>
</template>
