<template>
  <div
    class="eg-datepicker"
    :style="overlayStyle"
    @mousedown.prevent
    @keydown.stop="onKeyDown"
  >
    <!-- Text input — qo'lda sana yozish uchun, avtofocus -->
    <div class="eg-dp-input-wrap" @mousedown.stop>
      <input
        ref="textInput"
        class="eg-dp-input"
        :class="{ 'eg-dp-input--error': inputError }"
        :value="inputVal"
        :placeholder="format"
        autocomplete="off"
        spellcheck="false"
        @input="onTextInput"
        @keydown.stop="onInputKeyDown"
        @mousedown.stop
      />
    </div>

    <!-- Header: prev / Month Year / next -->
    <div class="eg-dp-header">
      <button class="eg-dp-nav" tabindex="-1" @mousedown.prevent="prevMonth">&#8249;</button>
      <span class="eg-dp-title">{{ t.months[viewMonth] }} {{ viewYear }}</span>
      <button class="eg-dp-nav" tabindex="-1" @mousedown.prevent="nextMonth">&#8250;</button>
    </div>

    <!-- Weekday labels + Day cells -->
    <div class="eg-dp-grid">
      <div v-for="wd in t.weekdays" :key="wd" class="eg-dp-wd">{{ wd }}</div>
      <div
        v-for="(cell, idx) in calCells"
        :key="idx"
        class="eg-dp-day"
        :class="{
          'eg-dp-day--empty':    !cell.day,
          'eg-dp-day--today':    cell.isToday,
          'eg-dp-day--selected': cell.isSelected,
          'eg-dp-day--focused':  cell.isFocused
        }"
        @mousedown.prevent="cell.day && pickDay(cell.day)"
      >{{ cell.day || '' }}</div>
    </div>

    <!-- Footer: Today / Clear -->
    <div class="eg-dp-footer">
      <button class="eg-dp-btn" tabindex="-1" @mousedown.prevent="pickToday">{{ t.today }}</button>
      <button class="eg-dp-btn eg-dp-btn--clear" tabindex="-1" @mousedown.prevent="pickClear">{{ t.clear }}</button>
    </div>
  </div>
</template>

<script>
import { parseDateValue, parseDateStrict, getFormatSpec, formatDate, toISO, todayISO, DEFAULT_DATE_FORMAT } from '../utils/dateUtils.js'
import { getMessages } from '../utils/i18n.js'

export default {
  name: 'EgDatePicker',

  props: {
    value:       { default: null },
    format:      { type: String, default: DEFAULT_DATE_FORMAT },
    locale:      { type: String, default: 'en' },
    triggerRect: { type: Object, default: null }
  },

  data() {
    const parsed = parseDateValue(this.value)
    const base   = parsed || new Date()
    return {
      viewYear:   base.getFullYear(),
      viewMonth:  base.getMonth(),
      focusDay:   parsed ? parsed.getDate() : null,
      inputVal:   parsed ? formatDate(parsed, this.format) : '',
      inputError: false
    }
  },

  computed: {
    t() { return getMessages(this.locale) },

    // ISO string of the currently typed/selected date (for calendar highlighting)
    _inputParsedISO() {
      const d = parseDateValue(this.inputVal)
      return d ? toISO(d) : null
    },

    calCells() {
      const year  = this.viewYear
      const month = this.viewMonth
      const today = todayISO()
      const selISO = this._inputParsedISO

      const offset = (new Date(year, month, 1).getDay() + 6) % 7
      const daysInMonth = new Date(year, month + 1, 0).getDate()

      const cells = []
      for (let i = 0; i < offset; i++) cells.push({ day: 0 })
      for (let d = 1; d <= daysInMonth; d++) {
        const iso = `${year}-${String(month + 1).padStart(2,'0')}-${String(d).padStart(2,'0')}`
        cells.push({
          day:        d,
          isToday:    iso === today,
          isSelected: iso === selISO,
          isFocused:  this.focusDay === d && iso !== selISO
        })
      }
      while (cells.length % 7 !== 0) cells.push({ day: 0 })
      return cells
    },

    overlayStyle() {
      const rect = this.triggerRect
      if (!rect) return { position: 'fixed', top: '50px', left: '50px', zIndex: 9999 }
      const calH = 330
      const spaceBelow = window.innerHeight - rect.bottom
      const top  = spaceBelow >= calH ? rect.bottom + 2 : rect.top - calH - 2
      const left = Math.min(rect.left, window.innerWidth - 224)
      return {
        position: 'fixed',
        top:  top + 'px',
        left: Math.max(4, left) + 'px',
        zIndex: 9999
      }
    }
  },

  mounted() {
    this.$nextTick(() => {
      if (this.$refs.textInput) {
        this.$refs.textInput.focus()
        this.$refs.textInput.select()
      }
    })
  },

  methods: {
    // ── Text input ──────────────────────────────────────────────
    onTextInput(e) {
      const isDeleting = e.inputType
        ? e.inputType.startsWith('delete')
        : e.target.value.length < this.inputVal.length

      this.inputVal   = isDeleting ? e.target.value : this._autoFillSep(e.target.value)
      this.inputError = false

      if (!isDeleting) {
        this.$nextTick(() => {
          const el = this.$refs.textInput
          if (el) el.setSelectionRange(this.inputVal.length, this.inputVal.length)
        })
      }

      const d = parseDateValue(this.inputVal)
      if (d) {
        this.viewYear  = d.getFullYear()
        this.viewMonth = d.getMonth()
        this.focusDay  = d.getDate()
      }

      // Raw, not-yet-committed keystroke — host debounces this itself
      // (col.onInput); 'pick' below is the final, validated commit.
      this.$emit('typing', this.inputVal)
    },

    _autoFillSep(raw) {
      const { sep, segments } = getFormatSpec(this.format)
      const maxDigits = segments.reduce((a, b) => a + b, 0)
      const digits = raw.replace(/\D/g, '').slice(0, maxDigits)

      let result = ''
      let pos = 0
      for (let i = 0; i < segments.length; i++) {
        const len   = segments[i]
        const chunk = digits.slice(pos, pos + len)
        result += chunk
        pos += len
        if (chunk.length === len && i < segments.length - 1) result += sep
      }
      return result
    },

    onInputKeyDown(e) {
      if (e.key === 'Enter') {
        e.preventDefault()
        this._commitInput()
      } else if (e.key === 'Escape') {
        e.preventDefault()
        this.$emit('close')
      } else if (e.key === 'Tab') {
        e.preventDefault()
        this._commitInput()
      }
      // Stop all keys from bubbling to the grid wrapper
    },

    _commitInput() {
      const val = this.inputVal.trim()
      if (val === '') { this.$emit('pick', ''); return }
      // Strict: must match THIS column's own format exactly (not just any
      // recognized format) and be a real calendar date — see parseDateStrict.
      const d = parseDateStrict(val, this.format)
      if (!d) { this.inputError = true; return }
      this.$emit('pick', formatDate(d, this.format))
    },

    // ── Month navigation ────────────────────────────────────────
    prevMonth() {
      if (this.viewMonth === 0) { this.viewMonth = 11; this.viewYear-- }
      else this.viewMonth--
      this._clampFocusDay()
    },
    nextMonth() {
      if (this.viewMonth === 11) { this.viewMonth = 0; this.viewYear++ }
      else this.viewMonth++
      this._clampFocusDay()
    },

    // ── Calendar day pick ───────────────────────────────────────
    pickDay(day) {
      if (!day) return
      const iso = `${this.viewYear}-${String(this.viewMonth + 1).padStart(2,'0')}-${String(day).padStart(2,'0')}`
      const d   = parseDateValue(iso)
      const formatted = formatDate(d, this.format)
      this.inputVal   = formatted
      this.inputError = false
      this.$emit('pick', formatted)
    },

    pickToday() {
      const d = new Date()
      const formatted = formatDate(d, this.format)
      this.inputVal   = formatted
      this.inputError = false
      this.$emit('pick', formatted)
    },

    pickClear() {
      this.inputVal   = ''
      this.inputError = false
      this.$emit('pick', '')
    },

    // ── Outer div keyboard (when input is NOT focused) ──────────
    onKeyDown(e) {
      const key = e.key
      if (key === 'Escape') { e.preventDefault(); this.$emit('close'); return }
      if (key === 'Enter' || key === ' ') {
        e.preventDefault()
        if (this.focusDay) this.pickDay(this.focusDay)
        return
      }
      if (key === 'PageUp')   { e.preventDefault(); this.prevMonth(); return }
      if (key === 'PageDown') { e.preventDefault(); this.nextMonth(); return }
      if (['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(key)) {
        e.preventDefault()
        this._moveFocus(key)
      }
    },

    _moveFocus(key) {
      const daysInMonth = new Date(this.viewYear, this.viewMonth + 1, 0).getDate()
      let day = this.focusDay || 1
      const delta = key === 'ArrowLeft' ? -1 : key === 'ArrowRight' ? 1 : key === 'ArrowUp' ? -7 : 7
      day += delta
      if (day < 1) {
        this.prevMonth()
        this.focusDay = Math.max(1, new Date(this.viewYear, this.viewMonth + 1, 0).getDate() + day + 1)
      } else if (day > daysInMonth) {
        this.nextMonth()
        this.focusDay = Math.min(day - daysInMonth, new Date(this.viewYear, this.viewMonth + 1, 0).getDate())
      } else {
        this.focusDay = day
      }
    },

    _clampFocusDay() {
      const daysInMonth = new Date(this.viewYear, this.viewMonth + 1, 0).getDate()
      if (this.focusDay && this.focusDay > daysInMonth) this.focusDay = daysInMonth
    }
  }
}
</script>

<style>
.eg-datepicker {
  background: var(--eg-color-bg, #fff);
  border: 1px solid var(--eg-color-border, #d0d0d0);
  border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0,0,0,.14);
  width: 216px;
  user-select: none;
  outline: none;
  font-size: 13px;
}

/* Text input */
.eg-dp-input-wrap {
  padding: 7px 7px 5px;
  border-bottom: 1px solid var(--eg-color-border, #e8e8e8);
}
.eg-dp-input {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--eg-color-border, #d0d0d0);
  border-radius: 4px;
  padding: 5px 8px;
  font-size: 13px;
  outline: none;
  color: var(--eg-color-text, #333);
  background: var(--eg-color-bg, #fff);
  user-select: text;
}
.eg-dp-input:focus {
  border-color: var(--eg-color-primary, #1565c0);
  box-shadow: 0 0 0 2px rgba(21,101,192,.15);
}
.eg-dp-input--error {
  border-color: var(--eg-color-danger, #c62828);
  box-shadow: 0 0 0 2px rgba(198,40,40,.12);
}

/* Header */
.eg-dp-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 6px 3px;
  border-bottom: 1px solid var(--eg-color-border, #e8e8e8);
}
.eg-dp-nav {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  padding: 0 6px;
  color: var(--eg-color-text, #333);
  border-radius: 3px;
}
.eg-dp-nav:hover { background: var(--eg-color-hover, #f0f0f0); }
.eg-dp-title { font-weight: 600; color: var(--eg-color-text, #333); }

/* Grid */
.eg-dp-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  padding: 4px 4px 2px;
  gap: 1px;
}
.eg-dp-wd {
  text-align: center;
  font-size: 11px;
  color: var(--eg-color-text-muted, #888);
  padding: 3px 0 4px;
  font-weight: 600;
}
.eg-dp-day {
  text-align: center;
  padding: 5px 2px;
  border-radius: 4px;
  cursor: pointer;
  color: var(--eg-color-text, #333);
  font-size: 12px;
  line-height: 1.4;
}
.eg-dp-day--empty { cursor: default; }
.eg-dp-day:not(.eg-dp-day--empty):hover { background: var(--eg-color-hover, #f0f0f0); }
.eg-dp-day--today { font-weight: 700; color: var(--eg-color-primary, #1565c0); }
.eg-dp-day--selected {
  background: var(--eg-color-primary, #1565c0);
  color: #fff;
  font-weight: 600;
}
.eg-dp-day--focused:not(.eg-dp-day--selected) {
  outline: 2px solid var(--eg-color-primary, #1565c0);
  outline-offset: -2px;
}

/* Footer */
.eg-dp-footer {
  display: flex;
  justify-content: space-between;
  padding: 4px 6px 6px;
  border-top: 1px solid var(--eg-color-border, #e8e8e8);
  gap: 4px;
}
.eg-dp-btn {
  flex: 1;
  background: none;
  border: 1px solid var(--eg-color-border, #d0d0d0);
  border-radius: 4px;
  padding: 4px 6px;
  cursor: pointer;
  font-size: 12px;
  color: var(--eg-color-text, #333);
}
.eg-dp-btn:hover { background: var(--eg-color-hover, #f0f0f0); }
.eg-dp-btn--clear { color: var(--eg-color-danger, #c62828); border-color: var(--eg-color-danger, #f1b0b0); }
.eg-dp-btn--clear:hover { background: #fff0f0; }
</style>
