/**
 * TyCalendar Web Component
 * PORTED FROM: cljs/ty/components/calendar.cljs
 *
 * A complete calendar orchestration component that combines navigation and month display.
 * Manages selection state, form participation, and event coordination.
 *
 * Architecture:
 * - year/month/day HTML attributes for intuitive API
 * - Internal state management (private properties)
 * - Distributes properties to child components (navigation + month)
 * - Single 'change' event with complete day context
 * - Form participation via ElementInternals
 *
 * Features:
 * - Combines ty-calendar-navigation + ty-calendar-month
 * - Date selection with visual feedback
 * - Form integration (works with FormData)
 * - Custom render functions (dayContentFn)
 * - Custom CSS injection
 * - Automatic date validation
 * - Event coordination between components
 *
 * @example
 * ```html
 * <!-- Basic usage -->
 * <ty-calendar
 *   year="2025"
 *   month="10"
 *   day="15">
 * </ty-calendar>
 *
 * <!-- Form integration -->
 * <form>
 *   <ty-calendar name="booking-date"></ty-calendar>
 *   <button type="submit">Submit</button>
 * </form>
 *
 * <!-- With custom rendering -->
 * <ty-calendar id="custom"></ty-calendar>
 * <script type="module">
 *   const cal = document.getElementById('custom');
 *   cal.dayContentFn = (ctx) => {
 *     const el = document.createElement('div');
 *     el.textContent = ctx.dayInMonth;
 *     if (ctx.today) el.style.fontWeight = 'bold';
 *     return el;
 *   };
 * </script>
 * ```
 */
import { ensureStyles } from '../utils/styles.js';
import { calendarStyles } from '../styles/calendar.js';
import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
// ============================================================================
// Helper Functions
// ============================================================================
/**
 * Get current date for defaults
 */
function getCurrentDate() {
    const now = new Date();
    return {
        year: now.getFullYear(),
        month: now.getMonth() + 1, // 1-based
        day: now.getDate(),
    };
}
/**
 * Validate and parse year from string
 */
function parseYear(yearStr) {
    if (!yearStr || !/^\d{4}$/.test(yearStr))
        return null;
    return parseInt(yearStr, 10);
}
/**
 * Validate and parse month from string (1-12)
 */
function parseMonth(monthStr) {
    if (!monthStr || !/^\d{1,2}$/.test(monthStr))
        return null;
    const month = parseInt(monthStr, 10);
    return month >= 1 && month <= 12 ? month : null;
}
/**
 * Validate and parse day from string (1-31, validated against month)
 */
function parseDay(dayStr, year, month) {
    if (!dayStr || !/^\d{1,2}$/.test(dayStr))
        return null;
    const day = parseInt(dayStr, 10);
    // Get days in month for validation
    const daysInMonth = new Date(year, month, 0).getDate();
    return day >= 1 && day <= daysInMonth ? day : null;
}
/**
 * Format date as ISO string (YYYY-MM-DD) for form submission
 */
function formatDateISO(year, month, day) {
    const monthStr = month.toString().padStart(2, '0');
    const dayStr = day.toString().padStart(2, '0');
    return `${year}-${monthStr}-${dayStr}`;
}
// ============================================================================
// Component Implementation
// ============================================================================
/**
 * TyCalendar Web Component
 */
export class TyCalendar extends HTMLElement {
    /**
     * Observed attributes
     */
    static get observedAttributes() {
        return ['year', 'month', 'day', 'show-navigation', 'stateless', 'locale', 'name', 'size', 'width'];
    }
    constructor() {
        super();
        this._locale = 'en-US';
        this._showNavigation = true;
        this._stateless = false;
        this._size = 'md';
        // Initialize state with current date
        const current = getCurrentDate();
        this._state = {
            displayYear: current.year,
            displayMonth: current.month,
        };
        this.attachShadow({ mode: 'open' });
        // Attach ElementInternals for form participation
        if ('attachInternals' in this) {
            this._internals = this.attachInternals();
        }
    }
    // ==========================================================================
    // Lifecycle Methods
    // ==========================================================================
    connectedCallback() {
        // Parse initial attributes
        this.initializeFromAttributes();
        // ✅ CHECK: Properties set before custom element upgrade
        // When scripts run before element is fully upgraded, properties are set as plain properties
        // without triggering setters. We need to migrate them to private fields.
        // Check for dayContentFn set before upgrade
        const plainDayContentFn = this.dayContentFn;
        if (plainDayContentFn && !this._dayContentFn) {
            console.log('📦 Migrating calendar dayContentFn set before upgrade');
            this._dayContentFn = plainDayContentFn;
            delete this.dayContentFn; // Clean up plain property
        }
        // Check for customCSS set before upgrade
        const plainCustomCSS = this.customCSS;
        if (plainCustomCSS && !this._customCSS) {
            console.log('📦 Migrating calendar customCSS set before upgrade');
            this._customCSS = plainCustomCSS;
            delete this.customCSS; // Clean up plain property
        }
        // Check for value set before upgrade
        const plainValue = this.value;
        if (plainValue && typeof plainValue === 'string' && plainValue !== this.value) {
            console.log('📦 Migrating calendar value set before upgrade:', plainValue);
            // Parse and set the value properly
            const match = plainValue.match(/^(\d{4})-(\d{2})-(\d{2})$/);
            if (match) {
                const year = parseInt(match[1], 10);
                const month = parseInt(match[2], 10);
                const day = parseInt(match[3], 10);
                this._state.selectedYear = year;
                this._state.selectedMonth = month;
                this._state.selectedDay = day;
                this._state.displayYear = year;
                this._state.displayMonth = month;
                // Update attributes if not in stateless mode
                if (!this._stateless) {
                    this.setAttribute('year', year.toString());
                    this.setAttribute('month', month.toString());
                    this.setAttribute('day', day.toString());
                }
            }
            delete this.value; // Clean up plain property
        }
        // Render the calendar
        this.render();
        // Set initial form value if date is selected
        this.updateFormValue();
        // Setup locale observer to watch for ancestor lang changes
        this._localeObserver = observeLocaleChanges(this, () => {
            this.render();
        });
    }
    disconnectedCallback() {
        // Cleanup locale observer
        if (this._localeObserver) {
            this._localeObserver();
            this._localeObserver = undefined;
        }
        // Cleanup references
        this._navigation = undefined;
        this._monthDisplay = undefined;
    }
    attributeChangedCallback(name, oldValue, newValue) {
        if (oldValue === newValue)
            return;
        switch (name) {
            case 'year':
            case 'month':
            case 'day':
                this.syncStateFromAttributes();
                this.syncChildComponents();
                this.updateFormValue();
                break;
            case 'show-navigation':
                this._showNavigation = newValue !== 'false';
                this.render();
                break;
            case 'stateless':
                this._stateless = newValue !== null && newValue !== 'false';
                // No re-render needed, just changes behavior
                break;
            case 'locale':
                this._locale = newValue || 'en-US';
                this.syncChildComponents();
                break;
            case 'size':
                if (newValue === 'sm' || newValue === 'md' || newValue === 'lg') {
                    this._size = newValue;
                    // Clear width when size is set (mutually exclusive)
                    this._width = undefined;
                    this.removeAttribute('width');
                    this.syncChildComponents();
                }
                break;
            case 'width':
                this._width = newValue || undefined;
                // Clear size when width is set (mutually exclusive)
                if (newValue) {
                    this._size = 'md'; // Reset to default
                    this.removeAttribute('size');
                }
                this.syncChildComponents();
                break;
            case 'name':
                // Name change triggers form value update
                this.updateFormValue();
                break;
        }
    }
    // ==========================================================================
    // Property Getters/Setters
    // ==========================================================================
    get year() {
        return this._state.selectedYear;
    }
    set year(value) {
        if (value !== undefined) {
            // In stateless mode, only update display
            if (this._stateless) {
                this._state.displayYear = value;
                this.setAttribute('year', value.toString());
            }
            else {
                // In stateful mode, update both selection and display
                this._state.selectedYear = value;
                this._state.displayYear = value;
                this.setAttribute('year', value.toString());
            }
        }
        else {
            if (!this._stateless) {
                delete this._state.selectedYear;
            }
            this.removeAttribute('year');
        }
        this.syncChildComponents();
        if (!this._stateless) {
            this.updateFormValue();
        }
    }
    get month() {
        return this._state.selectedMonth;
    }
    set month(value) {
        if (value !== undefined) {
            // In stateless mode, only update display
            if (this._stateless) {
                this._state.displayMonth = value;
                this.setAttribute('month', value.toString());
            }
            else {
                // In stateful mode, update both selection and display
                this._state.selectedMonth = value;
                this._state.displayMonth = value;
                this.setAttribute('month', value.toString());
            }
        }
        else {
            if (!this._stateless) {
                delete this._state.selectedMonth;
            }
            this.removeAttribute('month');
        }
        this.syncChildComponents();
        if (!this._stateless) {
            this.updateFormValue();
        }
    }
    get day() {
        return this._state.selectedDay;
    }
    set day(value) {
        if (value !== undefined) {
            this._state.selectedDay = value;
            this.setAttribute('day', value.toString());
        }
        else {
            delete this._state.selectedDay;
            this.removeAttribute('day');
        }
        this.syncChildComponents();
        this.updateFormValue();
    }
    get locale() {
        return getEffectiveLocale(this, this.getAttribute('locale'));
    }
    set locale(value) {
        this._locale = value;
        this.setAttribute('locale', value);
    }
    get showNavigation() {
        return this._showNavigation;
    }
    set showNavigation(value) {
        this._showNavigation = value;
        this.setAttribute('show-navigation', value.toString());
    }
    get stateless() {
        return this._stateless;
    }
    set stateless(value) {
        this._stateless = value;
        if (value) {
            this.setAttribute('stateless', '');
        }
        else {
            this.removeAttribute('stateless');
        }
    }
    get size() {
        return this._size;
    }
    set size(value) {
        if (this._size !== value) {
            this._size = value;
            // Clear width when size is set (mutually exclusive)
            this._width = undefined;
            this.removeAttribute('width');
            this.setAttribute('size', value);
            this.syncChildComponents();
        }
    }
    get width() {
        return this._width;
    }
    set width(value) {
        if (this._width !== value) {
            this._width = value;
            if (value) {
                // Clear size when width is set (mutually exclusive)
                this._size = 'md'; // Reset to default
                this.removeAttribute('size');
                this.setAttribute('width', value);
            }
            else {
                this.removeAttribute('width');
            }
            this.syncChildComponents();
        }
    }
    get dayContentFn() {
        return this._dayContentFn;
    }
    set dayContentFn(fn) {
        this._dayContentFn = fn;
        this.syncChildComponents();
    }
    get customCSS() {
        return this._customCSS;
    }
    set customCSS(sheet) {
        this._customCSS = sheet;
        this.syncChildComponents();
    }
    get value() {
        const { selectedYear, selectedMonth, selectedDay } = this._state;
        if (selectedYear && selectedMonth && selectedDay) {
            return formatDateISO(selectedYear, selectedMonth, selectedDay);
        }
        return '';
    }
    set value(isoDate) {
        if (!isoDate) {
            // Clear selection
            delete this._state.selectedYear;
            delete this._state.selectedMonth;
            delete this._state.selectedDay;
            // In normal mode, also clear attributes
            if (!this._stateless) {
                this.removeAttribute('year');
                this.removeAttribute('month');
                this.removeAttribute('day');
            }
        }
        else {
            // Parse ISO date (YYYY-MM-DD)
            const match = isoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/);
            if (match) {
                const year = parseInt(match[1], 10);
                const month = parseInt(match[2], 10);
                const day = parseInt(match[3], 10);
                // Update internal selection state
                this._state.selectedYear = year;
                this._state.selectedMonth = month;
                this._state.selectedDay = day;
                // Update display to show this month
                this._state.displayYear = year;
                this._state.displayMonth = month;
                // In normal mode, update attributes
                // In stateless mode, skip attribute updates (parent controls attributes)
                if (!this._stateless) {
                    this.setAttribute('year', year.toString());
                    this.setAttribute('month', month.toString());
                    this.setAttribute('day', day.toString());
                }
            }
        }
        this.syncChildComponents();
        // In normal mode, update form value
        if (!this._stateless) {
            this.updateFormValue();
        }
    }
    // ==========================================================================
    // State Management
    // ==========================================================================
    /**
     * Initialize state from HTML attributes on first load
     */
    initializeFromAttributes() {
        const yearStr = this.getAttribute('year');
        const monthStr = this.getAttribute('month');
        const dayStr = this.getAttribute('day');
        const localeStr = this.getAttribute('locale');
        const showNavStr = this.getAttribute('show-navigation');
        const statelessStr = this.getAttribute('stateless');
        const sizeStr = this.getAttribute('size');
        const widthStr = this.getAttribute('width');
        // Parse year
        const year = parseYear(yearStr);
        if (year) {
            this._state.displayYear = year;
        }
        // Parse month
        const month = parseMonth(monthStr);
        if (month) {
            this._state.displayMonth = month;
        }
        // Parse day (only if year and month are valid)
        if (year && month) {
            const day = parseDay(dayStr, year, month);
            if (day) {
                this._state.selectedYear = year;
                this._state.selectedMonth = month;
                this._state.selectedDay = day;
            }
        }
        // Locale
        if (localeStr) {
            this._locale = localeStr;
        }
        // Show navigation
        if (showNavStr) {
            this._showNavigation = showNavStr !== 'false';
        }
        // Stateless mode
        if (statelessStr !== null) {
            this._stateless = statelessStr !== 'false';
        }
        // Size
        if (sizeStr && (sizeStr === 'sm' || sizeStr === 'md' || sizeStr === 'lg')) {
            this._size = sizeStr;
        }
        // Width
        if (widthStr) {
            this._width = widthStr;
        }
    }
    /**
     * Sync state from changed attributes
     */
    syncStateFromAttributes() {
        const yearStr = this.getAttribute('year');
        const monthStr = this.getAttribute('month');
        const dayStr = this.getAttribute('day');
        const year = parseYear(yearStr);
        const month = parseMonth(monthStr);
        // Update display year/month
        if (year)
            this._state.displayYear = year;
        if (month)
            this._state.displayMonth = month;
        // Update selection (only if all three are valid)
        if (year && month) {
            const day = parseDay(dayStr, year, month);
            if (day) {
                this._state.selectedYear = year;
                this._state.selectedMonth = month;
                this._state.selectedDay = day;
            }
            else {
                // Clear selection if day is invalid
                delete this._state.selectedYear;
                delete this._state.selectedMonth;
                delete this._state.selectedDay;
            }
        }
        else {
            // Clear selection if year or month is invalid
            delete this._state.selectedYear;
            delete this._state.selectedMonth;
            delete this._state.selectedDay;
        }
    }
    /**
     * Update child components with current state
     */
    syncChildComponents() {
        // Update navigation
        if (this._navigation) {
            this._navigation.displayMonth = this._state.displayMonth;
            this._navigation.displayYear = this._state.displayYear;
            this._navigation.locale = this.locale;
            this._navigation.size = this._size;
            // Always sync width (set or clear)
            this._navigation.width = this._width;
        }
        // Update month display
        if (this._monthDisplay) {
            this._monthDisplay.displayMonth = this._state.displayMonth;
            this._monthDisplay.displayYear = this._state.displayYear;
            this._monthDisplay.locale = this.locale;
            this._monthDisplay.size = this._size;
            // Always sync width (set or clear)
            this._monthDisplay.width = this._width;
            // Pass render functions
            if (this._dayContentFn) {
                this._monthDisplay.dayContentFn = this._dayContentFn;
            }
            // Pass custom CSS
            if (this._customCSS) {
                this._monthDisplay.customCSS = this._customCSS;
            }
            // Update selection value (convert to Date timestamp)
            const { selectedYear, selectedMonth, selectedDay } = this._state;
            if (selectedYear && selectedMonth && selectedDay) {
                const date = new Date(selectedYear, selectedMonth - 1, selectedDay);
                this._monthDisplay.value = date.getTime();
            }
            else {
                this._monthDisplay.value = null;
            }
        }
    }
    /**
     * Update form value using ElementInternals
     */
    updateFormValue() {
        // Skip form participation in stateless mode
        if (this._stateless)
            return;
        if (!this._internals)
            return;
        const elementName = this.getAttribute('name');
        const { selectedYear, selectedMonth, selectedDay } = this._state;
        if (elementName && selectedYear && selectedMonth && selectedDay) {
            const isoDate = formatDateISO(selectedYear, selectedMonth, selectedDay);
            this._internals.setFormValue(isoDate);
        }
        else {
            this._internals.setFormValue('');
        }
    }
    // ==========================================================================
    // Event Handlers
    // ==========================================================================
    /**
     * Handle navigation change (month/year navigation)
     */
    handleNavigationChange(event) {
        event.preventDefault();
        event.stopPropagation();
        const { month, year } = event.detail;
        // Update display state
        this._state.displayMonth = month;
        this._state.displayYear = year;
        // Sync child components
        this.syncChildComponents();
        // Emit navigate event
        const navigateDetail = {
            month,
            year,
            action: 'navigate',
            source: 'navigation',
        };
        this.dispatchEvent(new CustomEvent('navigate', {
            detail: navigateDetail,
            bubbles: true,
            composed: true,
            cancelable: false,
        }));
    }
    /**
     * Handle day click (day selection)
     */
    handleDayClick(event) {
        event.preventDefault();
        event.stopPropagation();
        const { dayContext, year, month, day } = event.detail;
        // In stateless mode, just re-dispatch the event without updating internal state
        if (this._stateless) {
            // Re-dispatch day-click event for parent to handle
            this.dispatchEvent(new CustomEvent('day-click', {
                detail: event.detail,
                bubbles: true,
                composed: true,
                cancelable: true,
            }));
            return;
        }
        // Normal mode: update selection state
        this._state.selectedYear = year;
        this._state.selectedMonth = month;
        this._state.selectedDay = day;
        this._state.displayYear = year;
        this._state.displayMonth = month;
        // Update HTML attributes
        this.setAttribute('year', year.toString());
        this.setAttribute('month', month.toString());
        this.setAttribute('day', day.toString());
        // Update form value
        this.updateFormValue();
        // Sync child components
        this.syncChildComponents();
        // Emit change event
        const changeDetail = {
            year,
            month,
            day,
            action: 'select',
            source: 'day-click',
            dayContext,
        };
        this.dispatchEvent(new CustomEvent('change', {
            detail: changeDetail,
            bubbles: true,
            composed: true,
            cancelable: false,
        }));
    }
    // ==========================================================================
    // Public Methods
    // ==========================================================================
    /**
     * Force re-render of the calendar
     * Useful after updating dayContentFn or other dynamic properties
     */
    refresh() {
        this.syncChildComponents();
        // Force month display to re-render (for async data updates)
        if (this._monthDisplay && typeof this._monthDisplay.refresh === 'function') {
            this._monthDisplay.refresh();
        }
    }
    // ==========================================================================
    // Rendering
    // ==========================================================================
    /**
     * Create navigation element
     */
    createNavigation() {
        const nav = document.createElement('ty-calendar-navigation');
        // Set properties
        nav.displayMonth = this._state.displayMonth;
        nav.displayYear = this._state.displayYear;
        nav.locale = this.locale;
        nav.size = this._size;
        // Only set width if explicitly provided
        if (this._width) {
            nav.width = this._width;
        }
        // Listen for change events
        nav.addEventListener('change', (e) => this.handleNavigationChange(e));
        // Store reference
        this._navigation = nav;
        return nav;
    }
    /**
     * Create month display element
     */
    createMonthDisplay() {
        const month = document.createElement('ty-calendar-month');
        // Set properties
        month.displayMonth = this._state.displayMonth;
        month.displayYear = this._state.displayYear;
        month.locale = this.locale;
        month.size = this._size;
        // Only set width if explicitly provided
        if (this._width) {
            month.width = this._width;
        }
        // Set selection value if exists
        const { selectedYear, selectedMonth, selectedDay } = this._state;
        if (selectedYear && selectedMonth && selectedDay) {
            const date = new Date(selectedYear, selectedMonth - 1, selectedDay);
            month.value = date.getTime();
        }
        // Pass render functions
        if (this._dayContentFn) {
            month.dayContentFn = this._dayContentFn;
        }
        // Pass custom CSS
        if (this._customCSS) {
            month.customCSS = this._customCSS;
        }
        // Listen for day-click events
        month.addEventListener('day-click', (e) => this.handleDayClick(e));
        // Store reference
        this._monthDisplay = month;
        return month;
    }
    /**
     * Main render function
     */
    render() {
        const root = this.shadowRoot;
        if (!root)
            return;
        // Ensure styles are loaded
        ensureStyles(root, { css: calendarStyles, id: 'ty-calendar' });
        // Clear and rebuild
        root.innerHTML = '';
        // Create main container
        const container = document.createElement('div');
        container.className = 'calendar-container';
        // Add navigation if requested
        if (this._showNavigation) {
            const nav = this.createNavigation();
            container.appendChild(nav);
        }
        // Add month display
        const month = this.createMonthDisplay();
        container.appendChild(month);
        root.appendChild(container);
    }
}
/**
 * Form-associated custom element
 */
TyCalendar.formAssociated = true;
// Register the custom element
if (!customElements.get('ty-calendar')) {
    customElements.define('ty-calendar', TyCalendar);
}
//# sourceMappingURL=calendar.js.map