(() => {
    'use strict'
    let _CALENDAR

    // レコード詳細/新規作成/編集画面表示時にカレンダーを初期化
    kintone.events.on(
        ['app.record.detail.show', 'app.record.create.show', 'app.record.edit.show'],
        (event) => {
            kintone.app.record.setFieldShown('休日の生データ', false);
            initCalendarArea(
                event.record,
                getHolidayMap(event.record),
                event.type === 'app.record.detail.show'
            )
            return event
        }
    )

    // 年度フィールド変更時に各月の休日をクリアしてカレンダーを再描画
    kintone.events.on(
        [
            'app.record.create.change.年度',
            'app.record.edit.change.年度',
            'app.record.index.edit.change.年度'
        ],
        (event) => {
            forEachRawFields((field) => {
                event.record[field].value = ''
            })
            if (event.type !== 'app.record.index.edit.change.年度') {
                initCalendarArea(event.record)
            }
            return event
        }
    )

    // 一覧編集画面では各月の休日フィールドを編集不可にする
    kintone.events.on(
        ['app.record.index.edit.show'],
        (event) => {
            forEachRawFields((field) => {
                event.record[field].disabled = true
            })
            return event
        }
    )

    // レコード保存時にカレンダーで選択した休日を各月フィールドへ反映
    kintone.events.on(
        ['app.record.create.submit', 'app.record.edit.submit'],
        (event) => {
            const resultMap = _CALENDAR.getSelectedDatesMap()
            forEachRawFields((field, i) => {
                event.record[field].value = resultMap[i].join(',')
            })
            return event
        }
    )

    /* --------------------------------------------------
     * 初期化・データ準備
     * -------------------------------------------------- */
    // カレンダー表示エリアを初期化
    function initCalendarArea(record, map = {}, readonly = false) {
        const calendarAreaEl = kintone.app.record.getSpaceElement('calendar-area')
        calendarAreaEl.innerHTML = ''
        const year = record.年度.value
        if (isYearString(year)) {
            _CALENDAR = createCalendar(Number(year), map, readonly)
            calendarAreaEl.appendChild(_CALENDAR.fragment)
        }
    }

    // 既存レコードに保存されている休日を取得
    function getHolidayMap(record) {
        const res = {}
        forEachRawFields((field, i) => {
            const str = record[field].value
            if (!str) {
                return
            }
            res[i] = new Set(str.split(','))
        })
        return res
    }

    /* --------------------------------------------------
     * ユーティリティ
     * -------------------------------------------------- */
    // 各月フィールドをループ
    const forEachRawFields = (callback) => {
        for (let i = 1; i <= 12; i++) {
            callback(`休日_${i}月`, i)
        }
    }

    // ゼロパディング
    const padZero = (n) => (n < 10 ? '0' + n : n)

    // Date → yyyy-MM-dd
    const toYmd = (d) => `${d.getFullYear()}-${padZero(d.getMonth() + 1)}-${padZero(d.getDate())}`

    // 土日判定
    const isWeekend = (d) => d.getDay() === 0 || d.getDay() === 6

    // 年度文字列の妥当性チェック
    function isYearString(str) {
        // 文字列かつ空でない
        if (typeof str !== 'string' || str.length === 0) {
            return false
        }

        // 数字のみ
        if (!/^\d+$/.test(str)) {
            return false
        }

        // 4桁
        if (str.length !== 4) {
            return false
        }

        // 1900-3000 の範囲
        const year = parseInt(str, 10)
        return year >= 1900 && year <= 3000
    }

    /* --------------------------------------------------
     * カレンダー生成
     * -------------------------------------------------- */
    const createCalendar = (year, highlightMap, readonly) => {
        const fragment = document.createDocumentFragment()

        /* ----- 図例 ----- */
        const legend = document.createElement('div')
        legend.id = 'legend'
        legend.innerHTML = '<span class="box"></span>休日'
        fragment.appendChild(legend)

        /* ----- カレンダーコンテナ ----- */
        const calendar = document.createElement('div')
        calendar.id = 'calendar'
        if (readonly) {
            calendar.classList.add('readonly')
        }
        fragment.appendChild(calendar)

        /* ----- 内部状態 ----- */
        const selectedSet = new Set()
        const months = buildMonths(year)
        months.forEach((m) => {
            calendar.appendChild(buildMonth(m))
        })

        /* ---------- 月情報生成 ---------- */
        function buildMonths(year) {
            return Array.from({ length: 12 }, (_, i) => {
                const monthIndex = 3 + i // 0 基準月で 4 月始まり
                const date = new Date(year, monthIndex, 1)
                return {
                    displayMonth: date.getMonth() + 1,
                    year: date.getFullYear(),
                    month: date.getMonth(),
                    label: `${date.getFullYear()}年${date.getMonth() + 1}月`
                }
            })
        }

        /* ---------- 月単位カレンダー生成 ---------- */
        function buildMonth({ displayMonth, year, month, label }) {
            const monthBox = document.createElement('div')
            monthBox.className = 'month'

            buildTitle(monthBox, label)
            buildHeader(monthBox)

            const first = new Date(year, month, 1)
            buildPadBlocksStart(first, monthBox)
            buildDateBlocks(year, month, displayMonth, monthBox)
            buildPadBlocksEnd(monthBox)

            return monthBox
        }

        function buildTitle(monthBox, label) {
            const title = document.createElement('div')
            title.className = 'title'
            title.textContent = label
            monthBox.appendChild(title)
        }

        function buildHeader(monthBox) {
            ['日', '月', '火', '水', '木', '金', '土'].forEach((day, i) => {
                const cell = document.createElement('div')
                cell.className = 'cell header'
                addWeekendClass(cell, i)
                cell.textContent = day
                monthBox.appendChild(cell)
            })
        }

        function addWeekendClass(cell, week) {
            if (week === 0) {
                cell.classList.add('sun')
            } else if (week === 6) {
                cell.classList.add('sat')
            }
        }

        function buildPadBlocksStart(first, monthBox) {
            const startDayInWeek = first.getDay()
            for (let i = 0; i < startDayInWeek; i++) {
                const cell = document.createElement('div')
                cell.className = 'cell'
                monthBox.appendChild(cell)
            }
        }

        function buildDateBlocks(year, month, displayMonth, monthBox) {
            const last = new Date(year, month + 1, 0)
            const highlights = highlightMap[displayMonth] || null

            for (let day = 1; day <= last.getDate(); day++) {
                const dateObj = new Date(year, month, day)
                const ymd = toYmd(dateObj)
                const cell = document.createElement('div')
                cell.className = 'cell'
                cell.textContent = day
                cell.dataset.date = ymd

                addWeekendClass(cell, dateObj.getDay())

                // 初期選択判定
                let shouldSelect = false
                if (highlights === null) {
                    shouldSelect = isWeekend(dateObj)
                } else if (highlights instanceof Set) {
                    shouldSelect = highlights.has(ymd)
                }

                if (shouldSelect) {
                    cell.classList.add('selected')
                    selectedSet.add(ymd)
                }

                // 編集可モードのみクリックイベントを追加
                if (!readonly) {
                    cell.addEventListener('click', () => {
                        cell.classList.toggle('selected')
                        if (cell.classList.contains('selected')) {
                            selectedSet.add(ymd)
                        } else {
                            selectedSet.delete(ymd)
                        }
                    })
                }

                monthBox.appendChild(cell)
            }
        }

        function buildPadBlocksEnd(monthBox) {
            // 7×7 マスに揃えるための埋め草
            while (monthBox.children.length < 49) {
                const cell = document.createElement('div')
                cell.className = 'cell'
                monthBox.appendChild(cell)
            }
        }

        /* ---------- 選択済み日付取得 ---------- */
        function getSelectedDatesMap() {
            const res = {}
            months.forEach(({ month }) => {
                res[month + 1] = []
            })

            // 選択済み日付を振り分け
            selectedSet.forEach((ymd) => {
                const d = new Date(ymd)
                const displayMonth = d.getMonth() + 1
                res[displayMonth].push(ymd)
            })

            // 選択が無い月は全土日を自動追加
            Object.keys(res).forEach((monthKey) => {
                if (res[monthKey].length === 0) {
                    const monthInfo = months.find((m) => m.displayMonth == monthKey)
                    if (monthInfo) {
                        const year = monthInfo.year
                        const month = monthInfo.month
                        const lastDay = new Date(year, month + 1, 0).getDate()

                        for (let day = 1; day <= lastDay; day++) {
                            const dateObj = new Date(year, month, day)
                            if (isWeekend(dateObj)) {
                                res[monthKey].push(toYmd(dateObj))
                            }
                        }
                    }
                }
            })

            return res
        }

        return {
            fragment,
            getSelectedDatesMap
        }
    }
})()