<template>
  <ModalDialog :image-url="entity.image_url" :title="scheduleTitle" :subtitle="entity.name">
    <!-- Enable/disable toggle row -->
    <div class="schedule-toggle-row">
      <span class="toggle-label">{{ scheduleEnabled ? 'Enabled' : 'Paused' }}</span>
      <button
        type="button"
        class="toggle-track"
        :class="{ on: scheduleEnabled }"
        role="switch"
        :aria-checked="scheduleEnabled"
        @click="scheduleEnabled = !scheduleEnabled"
      >
        <span class="toggle-thumb" />
      </button>
    </div>

    <!-- Form body dims when paused -->
    <div class="schedule-body" :class="{ disabled: !scheduleEnabled }">
      <!-- Mode toggle -->
      <div class="mode-toggle">
        <button :class="['mode-btn', { active: mode === 'days' }]" @click="mode = 'days'">
          Specific Days
        </button>
        <button :class="['mode-btn', { active: mode === 'interval' }]" @click="mode = 'interval'">
          Every X Days
        </button>
      </div>

      <!-- Specific Days form -->
      <div v-if="mode === 'days'" class="days-form">
        <!-- Day chips -->
        <div class="day-chips">
          <button
            v-for="(label, idx) in DAY_CHIP_LABELS"
            :key="idx"
            type="button"
            :class="['chip', { active: selectedDays.has(idx) }]"
            @click="toggleDay(idx)"
          >
            {{ label }}
          </button>
        </div>

        <!-- Default deadline -->
        <div v-if="selectedDays.size > 0" class="default-deadline-row">
          <span class="field-label">Deadline</span>
          <TimePickerPopover
            v-if="hasDefaultDeadline"
            :modelValue="defaultTime"
            @update:modelValue="onDefaultTimeChanged"
          />
          <span v-else class="anytime-label">Anytime</span>
          <button type="button" class="link-btn" @click="hasDefaultDeadline = !hasDefaultDeadline">
            {{ hasDefaultDeadline ? 'Clear (Anytime)' : 'Set deadline' }}
          </button>
        </div>

        <!-- Selected day exception list -->
        <div v-if="selectedDays.size > 0" class="exception-list">
          <div v-for="idx in sortedSelectedDays" :key="idx" class="exception-row">
            <span class="exception-day-name">{{ DAY_LABELS[idx] }}</span>
            <div class="exception-right">
              <template v-if="exceptions.has(idx)">
                <TimePickerPopover
                  :modelValue="exceptions.get(idx)!"
                  @update:modelValue="(val) => exceptions.set(idx, val)"
                />
                <button type="button" class="link-btn reset-btn" @click="exceptions.delete(idx)">
                  Reset to default
                </button>
              </template>
              <template v-else>
                <span class="default-label">{{
                  hasDefaultDeadline ? formatDefaultTime : 'Anytime'
                }}</span>
                <button
                  type="button"
                  class="link-btn"
                  @click="exceptions.set(idx, { ...defaultTime })"
                >
                  Set different time
                </button>
              </template>
            </div>
          </div>
        </div>
      </div>

      <!-- Interval form -->
      <div v-else class="interval-form">
        <!-- Frequency stepper + anchor date -->
        <div class="interval-group">
          <div class="interval-row">
            <label class="field-label">Every</label>
            <div class="stepper">
              <button
                type="button"
                class="stepper-btn"
                @click="decrementInterval"
                :disabled="intervalDays <= 1"
              >
                −
              </button>
              <span class="stepper-value">{{ intervalDays }}</span>
              <button
                type="button"
                class="stepper-btn"
                @click="incrementInterval"
                :disabled="intervalDays >= 7"
              >
                +
              </button>
            </div>
            <span class="field-label">{{ intervalDays === 1 ? 'day' : 'days' }}</span>
          </div>
          <div class="interval-row">
            <label class="field-label">Starting on</label>
            <DateInputField
              :modelValue="anchorDate"
              :min="todayISO"
              @update:modelValue="anchorDate = $event"
            />
          </div>
        </div>

        <!-- Next occurrence preview -->
        <div v-if="nextOccurrence" class="next-occurrence-row">
          <span class="next-occurrence-label">Next occurrence: {{ nextOccurrence }}</span>
        </div>

        <!-- Deadline row -->
        <div class="interval-time-row">
          <label class="field-label">Deadline</label>
          <TimePickerPopover
            v-if="hasDeadline"
            :modelValue="intervalTime"
            @update:modelValue="intervalTime = $event"
          />
          <span v-else class="anytime-label">Anytime</span>
          <button type="button" class="link-btn" @click="toggleDeadline">
            {{ hasDeadline ? 'Clear (Anytime)' : 'Set deadline' }}
          </button>
        </div>
      </div>

      <p v-if="errorMsg" class="error-msg">{{ errorMsg }}</p>
    </div>
    <!-- /schedule-body -->

    <!-- Actions outside dim wrapper so they stay interactive -->
    <div class="modal-actions">
      <button class="btn btn-secondary" @click="$emit('cancelled')" :disabled="saving">
        Cancel
      </button>
      <button class="btn-primary" @click="save" :disabled="saving || !isValid || !isDirty">
        {{ saving ? 'Saving…' : 'Save' }}
      </button>
    </div>
  </ModalDialog>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import ModalDialog from './ModalDialog.vue'
import TimePickerPopover from './TimePickerPopover.vue'
import DateInputField from './DateInputField.vue'
import {
  setChoreSchedule,
  deleteChoreSchedule,
  setRoutineSchedule,
  deleteRoutineSchedule,
  parseErrorResponse,
} from '@/common/api'
import type {
  ChildTask,
  ChildRoutine,
  ChoreSchedule,
  RoutineSchedule,
  DayConfig,
} from '@/common/models'

interface TimeValue {
  hour: number
  minute: number
}

const props = defineProps<{
  entity: ChildTask | ChildRoutine
  entityType: 'task' | 'routine'
  childId: string
  schedule: ChoreSchedule | RoutineSchedule | null
}>()

const scheduleTitle = computed(() =>
  props.entityType === 'task' ? 'Schedule Chore' : 'Schedule Routine',
)

const emit = defineEmits<{
  (e: 'saved'): void
  (e: 'cancelled'): void
}>()

const DAY_LABELS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
const DAY_CHIP_LABELS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']

// ── local state ──────────────────────────────────────────────────────────────

const mode = ref<'days' | 'interval'>(props.schedule?.mode ?? 'days')

// days mode — three-layer state
function initDaysState(schedule: ChoreSchedule | null): {
  days: Set<number>
  base: TimeValue
  exMap: Map<number, TimeValue>
} {
  const days = new Set<number>()
  const exMap = new Map<number, TimeValue>()
  let base: TimeValue = { hour: 8, minute: 0 }

  if (schedule?.mode === 'days') {
    // Load the persisted default time (falls back to 8:00 AM for old records)
    base = { hour: schedule.default_hour ?? 8, minute: schedule.default_minute ?? 0 }
    for (const dc of schedule.day_configs) {
      days.add(dc.day)
      if (dc.hour !== base.hour || dc.minute !== base.minute) {
        exMap.set(dc.day, { hour: dc.hour, minute: dc.minute })
      }
    }
  }

  return { days, base, exMap }
}

const { days: _days, base: _base, exMap: _exMap } = initDaysState(props.schedule)
const selectedDays = ref<Set<number>>(_days)
const defaultTime = ref<TimeValue>(_base)
const exceptions = ref<Map<number, TimeValue>>(_exMap)
const hasDefaultDeadline = ref<boolean>(props.schedule?.default_has_deadline ?? true)
const scheduleEnabled = ref<boolean>(props.schedule?.enabled ?? true)

// ── helpers (date) ───────────────────────────────────────────────────────────

function getTodayISO(): string {
  const d = new Date()
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}

// interval mode
const intervalDays = ref<number>(Math.max(1, props.schedule?.interval_days ?? 1))
const anchorDate = ref<string>(props.schedule?.anchor_date || getTodayISO())
const hasDeadline = ref<boolean>(props.schedule?.interval_has_deadline ?? true)
const intervalTime = ref<TimeValue>({
  hour: props.schedule?.interval_hour ?? 8,
  minute: props.schedule?.interval_minute ?? 0,
})

const saving = ref(false)
const errorMsg = ref<string | null>(null)

// ── original snapshot (for dirty detection) ──────────────────────────────────

const origMode = props.schedule?.mode ?? 'days'
const origDays = new Set(_days)
const origDefaultTime: TimeValue = { ..._base }
const origHasDefaultDeadline = props.schedule?.default_has_deadline ?? true
const origExceptions = new Map<number, TimeValue>(
  Array.from(_exMap.entries()).map(([k, v]) => [k, { ...v }]),
)
const origIntervalDays = Math.max(1, props.schedule?.interval_days ?? 1)
const origAnchorDate = props.schedule?.anchor_date || getTodayISO()
const origHasDeadline = props.schedule?.interval_has_deadline ?? true
const origIntervalTime: TimeValue = {
  hour: props.schedule?.interval_hour ?? 8,
  minute: props.schedule?.interval_minute ?? 0,
}
const origEnabled = props.schedule?.enabled ?? true

// ── computed ─────────────────────────────────────────────────────────────────

const sortedSelectedDays = computed(() => Array.from(selectedDays.value).sort((a, b) => a - b))

const todayISO = getTodayISO()

const nextOccurrence = computed((): string | null => {
  if (!anchorDate.value) return null
  const [y, m, d] = anchorDate.value.split('-').map(Number)
  const anchor = new Date(y, m - 1, d, 0, 0, 0, 0)
  if (isNaN(anchor.getTime())) return null
  const today = new Date()
  today.setHours(0, 0, 0, 0)

  const fmt = (dt: Date) =>
    dt.toLocaleDateString('en-US', {
      weekday: 'short',
      month: 'short',
      day: 'numeric',
      year: 'numeric',
    })

  // Find the first hit date strictly after today
  for (let i = 1; i <= 365; i++) {
    const cur = new Date(anchor.getTime() + i * 86_400_000)
    const diffDays = Math.round((cur.getTime() - anchor.getTime()) / 86_400_000)
    if (diffDays % intervalDays.value === 0 && cur >= today) {
      return fmt(cur)
    }
  }
  return null
})

const formatDefaultTime = computed(() => {
  const h = defaultTime.value.hour % 12 || 12
  const m = String(defaultTime.value.minute).padStart(2, '0')
  const period = defaultTime.value.hour < 12 ? 'AM' : 'PM'
  return `${String(h).padStart(2, '0')}:${m} ${period}`
})

// ── helpers ──────────────────────────────────────────────────────────────────

function toggleDay(dayIdx: number) {
  if (selectedDays.value.has(dayIdx)) {
    selectedDays.value.delete(dayIdx)
    exceptions.value.delete(dayIdx)
    // trigger reactivity
    selectedDays.value = new Set(selectedDays.value)
  } else {
    const next = new Set(selectedDays.value)
    next.add(dayIdx)
    selectedDays.value = next
  }
}

function onDefaultTimeChanged(val: TimeValue) {
  defaultTime.value = val
}

function decrementInterval() {
  if (intervalDays.value > 1) intervalDays.value--
}

function incrementInterval() {
  if (intervalDays.value < 7) intervalDays.value++
}

function toggleDeadline() {
  hasDeadline.value = !hasDeadline.value
}

// ── validation + dirty ───────────────────────────────────────────────────────

const isValid = computed(() => {
  // days mode: 0 selections is valid — means "unschedule" (always active)
  if (mode.value === 'interval') {
    return intervalDays.value >= 1 && intervalDays.value <= 7
  }
  return true
})

const isDirty = computed(() => {
  if (scheduleEnabled.value !== origEnabled) return true
  if (mode.value !== origMode) return true

  if (mode.value === 'days') {
    // Selected days set changed
    if (selectedDays.value.size !== origDays.size) return true
    for (const d of selectedDays.value) {
      if (!origDays.has(d)) return true
    }
    // Default time changed
    if (
      defaultTime.value.hour !== origDefaultTime.hour ||
      defaultTime.value.minute !== origDefaultTime.minute
    )
      return true
    // Default deadline toggled
    if (hasDefaultDeadline.value !== origHasDefaultDeadline) return true
    // Exceptions changed
    if (exceptions.value.size !== origExceptions.size) return true
    for (const [day, t] of exceptions.value) {
      const orig = origExceptions.get(day)
      if (!orig || orig.hour !== t.hour || orig.minute !== t.minute) return true
    }
    return false
  } else {
    if (intervalDays.value !== origIntervalDays) return true
    if (anchorDate.value !== origAnchorDate) return true
    if (hasDeadline.value !== origHasDeadline) return true
    if (
      hasDeadline.value &&
      (intervalTime.value.hour !== origIntervalTime.hour ||
        intervalTime.value.minute !== origIntervalTime.minute)
    )
      return true
    return false
  }
})

// ── save ─────────────────────────────────────────────────────────────────────

async function save() {
  if (!isValid.value || !isDirty.value || saving.value) return
  errorMsg.value = null
  saving.value = true

  const entityId = props.entity.id
  const saveEntitySchedule = (payload: object) =>
    props.entityType === 'task'
      ? setChoreSchedule(props.childId, entityId, payload)
      : setRoutineSchedule(props.childId, entityId, payload)
  const deleteEntitySchedule = () =>
    props.entityType === 'task'
      ? deleteChoreSchedule(props.childId, entityId)
      : deleteRoutineSchedule(props.childId, entityId)

  let res: Response
  if (mode.value === 'days' && selectedDays.value.size === 0) {
    // 0 days = remove schedule entirely so the item becomes always active
    res = await deleteEntitySchedule()
  } else if (mode.value === 'days') {
    const day_configs: DayConfig[] = Array.from(selectedDays.value).map((day) => {
      const override = exceptions.value.get(day)
      return {
        day,
        hour: override?.hour ?? defaultTime.value.hour,
        minute: override?.minute ?? defaultTime.value.minute,
      }
    })
    res = await saveEntitySchedule({
      mode: 'days',
      enabled: scheduleEnabled.value,
      day_configs,
      default_hour: defaultTime.value.hour,
      default_minute: defaultTime.value.minute,
      default_has_deadline: hasDefaultDeadline.value,
    })
  } else {
    res = await saveEntitySchedule({
      mode: 'interval',
      enabled: scheduleEnabled.value,
      interval_days: intervalDays.value,
      anchor_date: anchorDate.value,
      interval_has_deadline: hasDeadline.value,
      interval_hour: intervalTime.value.hour,
      interval_minute: intervalTime.value.minute,
    })
  }
  saving.value = false

  if (res.ok) {
    emit('saved')
  } else {
    const { msg } = await parseErrorResponse(res)
    errorMsg.value = msg
  }
}
</script>

<style scoped>
.mode-toggle {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1.2rem;
}

.mode-btn {
  flex: 1;
  padding: 0.55rem 0.4rem;
  border: 1.5px solid var(--primary, #667eea);
  border-radius: 6px;
  background: transparent;
  color: var(--primary, #667eea);
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s;
}

.mode-btn.active {
  background: var(--btn-primary, #667eea);
  color: #fff;
}

/* Days form */
.days-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1rem;
}

.day-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.chip {
  min-width: 2.4rem;
  padding: 0.4rem 0.5rem;
  border: 1.5px solid var(--primary, #667eea);
  border-radius: 999px;
  background: transparent;
  color: var(--primary, #667eea);
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
  text-align: center;
  transition:
    background 0.15s,
    color 0.15s;
  font-family: inherit;
}

.chip:hover {
  background: color-mix(in srgb, var(--btn-primary, #667eea) 12%, transparent);
}

.chip.active {
  background: var(--btn-primary, #667eea);
  color: #fff;
}

.default-deadline-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.exception-list {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.exception-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.exception-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
}

.exception-day-name {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--secondary, #7257b3);
  min-width: 75px;
}

.default-label {
  font-size: 0.85rem;
  color: var(--form-label, #888);
  font-style: italic;
}

.link-btn {
  display: inline-flex;
  align-items: center;
  background: none;
  border: none;
  padding: 0.6rem 0.5rem;
  margin: -0.6rem -0.5rem;
  min-height: 44px;
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary, #667eea);
  text-decoration: underline;
  font-family: inherit;
  transition: opacity 0.12s;
  white-space: nowrap;
}

.link-btn:hover {
  opacity: 0.75;
}

.reset-btn {
  color: var(--error, #e53e3e);
}

/* Interval form */
.interval-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1rem;
}

.interval-group {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.interval-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.interval-time-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.field-label {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--secondary, #7257b3);
}

.stepper {
  display: flex;
  align-items: center;
  gap: 0;
  border: 1.5px solid var(--primary, #667eea);
  border-radius: 6px;
  overflow: hidden;
  flex-shrink: 0;
}

.stepper-btn {
  min-width: 2.75rem;
  min-height: 2.75rem;
  background: transparent;
  border: none;
  color: var(--primary, #667eea);
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.12s;
  font-family: inherit;
  display: flex;
  align-items: center;
  justify-content: center;
}

.stepper-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--btn-primary, #667eea) 12%, transparent);
}

.stepper-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.stepper-value {
  min-width: 1.6rem;
  text-align: center;
  font-size: 1rem;
  font-weight: 700;
  color: var(--secondary, #7257b3);
  padding: 0 0.25rem;
}

.anytime-label {
  font-size: 0.85rem;
  color: var(--form-label, #888);
  font-style: italic;
}

.next-occurrence-row {
  display: flex;
  align-items: center;
}

.next-occurrence-label {
  font-size: 0.85rem;
  color: var(--form-label, #888);
  font-style: italic;
}

/* Error */
.error-msg {
  color: var(--error, #e53e3e);
  font-size: 0.9rem;
  margin-bottom: 0.5rem;
}

/* Actions */
.modal-actions {
  display: flex;
  gap: 1rem;
  justify-content: center;
  margin-top: 1rem;
}

.btn-primary {
  background: var(--btn-primary, #667eea);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 0.55rem 1.4rem;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}

.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ── Enable/Disable Toggle ────────────────────── */

.schedule-toggle-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-bottom: 1rem;
  min-height: 44px;
}

.toggle-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--form-label, #444);
  user-select: none;
}

.toggle-track {
  position: relative;
  width: 48px;
  height: 24px;
  border-radius: 999px;
  border: none;
  background: var(--form-input-border, #cbd5e1);
  cursor: pointer;
  transition: background 0.2s ease;
  padding: 0;
  flex-shrink: 0;
}

.toggle-track.on {
  background: var(--btn-primary, #667eea);
}

.toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  transition: transform 0.2s ease;
}

.toggle-track.on .toggle-thumb {
  transform: translateX(24px);
}

/* ── Dim wrapper when paused ──────────────────── */

.schedule-body {
  transition: opacity 0.2s ease;
}

.schedule-body.disabled {
  opacity: 0.45;
  pointer-events: none;
}
</style>
