{% extends "base.html" %}
{% from "components/ui.html" import page_header, breadcrumb_nav, button, filter_badge, form_group, form_select, form_textarea, form_checkbox, form_date, form_section, form_actions %}
{% from "components/client_select.html" import client_select %}

{% block content %}
{% set breadcrumbs = [
    {'text': _('Time Tracking')},
    {'text': _('Log Time') if not is_duplicate else _('Duplicate Entry')}
] %}

{{ page_header(
    icon_class='fas fa-clock',
    title_text=_('Duplicate Time Entry') if is_duplicate else _('Log Time Manually'),
    subtitle_text=_('Create a copy of a previous entry with new times') if is_duplicate else _('Create a new time entry'),
    breadcrumbs=breadcrumbs,
    actions_html=None
) }}

{% if is_duplicate and original_entry %}
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-xl p-4 mb-6 max-w-3xl mx-auto">
    <div class="flex items-start gap-3">
        <i class="fas fa-info-circle text-blue-600 dark:text-blue-400 mt-1"></i>
        <div>
            <p class="text-sm text-blue-800 dark:text-blue-200">
                <strong>{{ _('Duplicating entry:') }}</strong> 
                {% if original_entry.project %}
                    {{ original_entry.project.name }}{% if original_entry.task %} - {{ original_entry.task.name }}{% endif %}
                {% elif original_entry.client %}
                    {{ original_entry.client.name }} <span class="text-xs text-gray-500">({{ _('Direct') }})</span>
                {% endif %}
            </p>
            <p class="text-xs text-blue-600 dark:text-blue-300 mt-1">
                {{ _('Original:') }} {{ original_entry.start_time|user_datetime }} {{ _('to') }} {{ original_entry.end_time|user_datetime if original_entry.end_time else _('N/A') }} ({{ original_entry.duration_formatted }})
            </p>
        </div>
    </div>
</div>
{% endif %}

<div class="bg-card-light dark:bg-card-dark p-6 rounded-xl shadow-lg max-w-3xl mx-auto">
    <form method="POST" action="{{ url_for('timer.manual_entry') }}" autocomplete="off" novalidate data-validate-form id="manualEntryForm"
          data-require-task="{{ 'true' if getattr(settings, 'time_entry_require_task', false) else 'false' }}"
          data-require-description="{{ 'true' if getattr(settings, 'time_entry_require_description', false) else 'false' }}"
          data-description-min-length="{{ getattr(settings, 'time_entry_description_min_length', 20) }}"
          data-description-min-msg="{{ _('Description must be at least %(min)s characters', min=getattr(settings, 'time_entry_description_min_length', 20))|e }}"
          data-break-after-hours-1="{{ getattr(settings, 'break_after_hours_1', none) or 6 }}"
          data-break-minutes-1="{{ getattr(settings, 'break_minutes_1', none) or 30 }}"
          data-break-after-hours-2="{{ getattr(settings, 'break_after_hours_2', none) or 9 }}"
          data-break-minutes-2="{{ getattr(settings, 'break_minutes_2', none) or 45 }}">
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
        <input type="hidden" name="worked_time_mode" id="worked_time_mode" value="{{ prefill_worked_time_mode or '' }}">

        <div class="space-y-8">
            {# Section: Project & task #}
            <section class="pb-6 border-b border-border-light dark:border-border-dark">
                <h2 class="form-section-title">
                    <i class="fas fa-folder text-primary"></i>{{ _('Project & task') }}
                </h2>
                <div class="space-y-6">
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                        <div>
                            <label for="project_id" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Project') }}</label>
                            <select name="project_id" id="project_id" class="form-input">
                                <option value="">{{ _('Select a project (optional)') }}</option>
                                {% for project in projects %}
                                <option value="{{ project.id }}" {% if selected_project_id == project.id %}selected{% endif %}>{{ project.name }}</option>
                                {% endfor %}
                            </select>
                        </div>
                        <div>
                            <label for="client_id" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Client') }}</label>
                            {# Single client: show as normal dropdown, pre-select only when client_id is in URL (parameter) #}
                            {{ client_select('client_id', clients, selected_id=selected_client_id, required=False, only_one_client=false, single_client=none) }}
                            <p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Select either a project or a client') }}</p>
                        </div>
                    </div>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                        <div>
                            <label for="task_id" class="block text-sm font-medium text-text-light dark:text-text-dark">{% if getattr(settings, 'time_entry_require_task', false) %}{{ _('Task') }} *{% else %}{{ _('Task (optional)') }}{% endif %}</label>
                            <select name="task_id" id="task_id" class="form-input" data-selected-task-id="{{ selected_task_id or '' }}" {% if not selected_project_id %}disabled{% endif %}>
                                <option value="">{{ _('No task') }}</option>
                            </select>
                            <p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Tasks load after selecting a project') }}</p>
                        </div>
                    </div>
                </div>
            </section>

            {# Section: Date & time #}
            <section class="pb-6 border-b border-border-light dark:border-border-dark">
                <h2 class="form-section-title">
                    <i class="fas fa-calendar-alt text-primary"></i>{{ _('Date & time') }}
                </h2>
                <div class="space-y-6">
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                        <div>
                            <label for="start_date" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Start Date') }}</label>
                            <input type="date" name="start_date" id="start_date" class="form-input user-date-input" value="{{ prefill_start_date or '' }}">
                        </div>
                        <div>
                            <label for="end_date" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('End Date') }}</label>
                            <input type="date" name="end_date" id="end_date" class="form-input user-date-input" value="{{ prefill_end_date or '' }}">
                        </div>
                    </div>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                        <div>
                            <label for="start_time" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Start Time') }}</label>
                            <input type="time" name="start_time" id="start_time" class="form-input" value="{{ prefill_start_time or '' }}">
                        </div>
                        <div>
                            <label for="end_time" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('End Time') }}</label>
                            <input type="time" name="end_time" id="end_time" class="form-input" value="{{ prefill_end_time or '' }}">
                        </div>
                    </div>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                        <div>
                            <label for="worked_time" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Worked Time') }}</label>
                            <input type="text" name="worked_time" id="worked_time" class="form-input" inputmode="numeric" placeholder="HH:MM" autocomplete="off" value="{{ prefill_worked_time or '' }}">
                            <p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Optional: enter HH:MM for duration. You can combine with Start Date/Time to log time on a specific day.') }}</p>
                        </div>
                        <div>
                            <label for="break_time" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Break') }}</label>
                            <div class="flex items-center gap-2">
                                <input type="text" name="break_time" id="break_time" class="form-input flex-1" inputmode="numeric" placeholder="HH:MM" autocomplete="off" value="{{ prefill_break_time or '' }}">
                                <button type="button" id="suggestBreakBtn" class="px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-text-light dark:text-text-dark text-sm hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors" title="{{ _('Suggest break based on duration (e.g. >6h: 30 min, >9h: 45 min)') }}">{{ _('Suggest') }}</button>
                            </div>
                            <p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Optional: break duration (HH:MM). Subtracted from total time to get worked duration.') }}</p>
                        </div>
                    </div>
                </div>
            </section>

            {# Section: Details #}
            <section>
                <h2 class="text-sm font-semibold text-text-light dark:text-text-dark uppercase tracking-wide flex items-center gap-2 mb-4">
                    <i class="fas fa-align-left text-primary"></i>{{ _('Details') }}
                </h2>
                <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
                    <div class="md:col-span-2">
                        <label for="notes" class="block text-sm font-medium text-text-light dark:text-text-dark mb-2">{% if getattr(settings, 'time_entry_require_description', false) %}{{ _('Notes') }} *{% else %}{{ _('Notes') }}{% endif %}</label>
                        <div class="markdown-editor-wrapper">
                            <textarea name="notes" id="notes" class="hidden">{{ prefill_notes or '' }}</textarea>
                            <div id="notes_editor"></div>
                        </div>
                    </div>
                    <div class="space-y-4">
                        <div>
                            <label for="tags" class="block text-sm font-medium text-text-light dark:text-text-dark">{{ _('Tags') }}</label>
                            <input type="text" name="tags" id="tags" class="form-input" placeholder="{{ _('tag1, tag2') }}" value="{{ prefill_tags or '' }}">
                        </div>
                        <div class="flex items-center gap-3">
                            <input type="checkbox" id="billable" name="billable" class="h-5 w-5 rounded border-gray-300 text-primary focus:ring-0" {% if prefill_billable is not defined or prefill_billable %}checked{% endif %}>
                            <label for="billable" class="text-sm font-medium text-text-light dark:text-text-dark">{{ _('Billable') }}</label>
                        </div>
                    </div>
                </div>
            </section>
        </div>

        <div class="mt-8 border-t border-border-light dark:border-border-dark pt-6 flex flex-col sm:flex-row justify-end gap-3">
            <button type="reset" class="w-full sm:w-auto px-5 py-2.5 rounded-lg border border-border-light dark:border-border-dark text-text-light dark:text-text-dark hover:bg-background-light dark:hover:bg-background-dark font-medium transition-all">{{ _('Clear') }}</button>
            <button type="submit" class="w-full sm:w-auto bg-primary hover:bg-primary-dark text-white px-5 py-2.5 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-all duration-200">{{ _('Log Time') }}</button>
        </div>
    </form>
</div>
<script>
(function() {
    // Issue #557: avoid loading Toast UI Editor on mobile to prevent browser freeze
    window.isMobileView = window.matchMedia('(max-width: 767px)').matches;
    if (window.isMobileView) {
        var notes = document.getElementById('notes');
        var editorEl = document.getElementById('notes_editor');
        if (notes) {
            notes.classList.remove('hidden');
            notes.classList.add('form-input');
            notes.setAttribute('rows', '5');
        }
        if (editorEl) editorEl.style.display = 'none';
    }
})();
</script>
<script>
document.addEventListener('DOMContentLoaded', async function(){
    // Default values for date/time to now (use local date, not UTC, for timezones ahead of UTC)
    const now = new Date();
    const yyyy = now.getFullYear();
    const monthPad = String(now.getMonth() + 1).padStart(2, '0');
    const dayPad = String(now.getDate()).padStart(2, '0');
    const today = `${yyyy}-${monthPad}-${dayPad}`;
    const hh = String(now.getHours()).padStart(2,'0');
    const mm = String(now.getMinutes()).padStart(2,'0');
    const startDate = document.getElementById('start_date');
    const endDate = document.getElementById('end_date');
    const startTime = document.getElementById('start_time');
    const endTime = document.getElementById('end_time');
    const workedTime = document.getElementById('worked_time');
    // Only auto-fill start/end if user isn't trying to do duration-only.
    const hasWorkedPrefill = !!(workedTime && String(workedTime.value || '').trim());
    if (!hasWorkedPrefill) {
        if (startDate && !startDate.value) startDate.value = today;
        if (endDate && !endDate.value) endDate.value = today;
        if (startTime && !startTime.value) startTime.value = `${hh}:${mm}`;
        if (endTime && !endTime.value) endTime.value = `${hh}:${mm}`;
    }

    // Worked time helper (duration)
    const workedTimeMode = document.getElementById('worked_time_mode');
    let suppressTimeSync = false;
    let workedTimeUserEdited = false;
    let timeFieldsUserEdited = false;

    function setDurationOnlyMode(on) {
        // No longer disable date/time when duration is entered: allow combined use
        // (e.g. "2:30" + "yesterday" = 2.5h on that date). Only track mode for backend.
        if (workedTimeMode) workedTimeMode.value = on ? 'explicit' : (workedTimeMode.value || '');
    }

    function ensureStartEndDefaultsIfEmpty() {
        if (startDate && !startDate.value) startDate.value = today;
        if (endDate && !endDate.value) endDate.value = today;
        if (startTime && !startTime.value) startTime.value = `${hh}:${mm}`;
        if (endTime && !endTime.value) endTime.value = `${hh}:${mm}`;
    }

    function parseWorkedMinutes(raw) {
        const s = String(raw || '').trim();
        if (!s) return null;
        const m = s.match(/^(\d{1,3}):([0-5]\d)$/);
        if (!m) return null;
        const hours = parseInt(m[1], 10);
        const minutes = parseInt(m[2], 10);
        if (Number.isNaN(hours) || Number.isNaN(minutes)) return null;
        return hours * 60 + minutes;
    }

    function formatWorkedMinutes(totalMinutes) {
        const mins = Math.max(0, Math.floor(totalMinutes || 0));
        const h = Math.floor(mins / 60);
        const m = mins % 60;
        return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}`;
    }

    function getStartEnd() {
        if (!startDate || !startTime || !endDate || !endTime) return { start: null, end: null };
        if (!startDate.value || !startTime.value || !endDate.value || !endTime.value) return { start: null, end: null };
        const start = new Date(`${startDate.value}T${startTime.value}:00`);
        const end = new Date(`${endDate.value}T${endTime.value}:00`);
        if (isNaN(start.getTime()) || isNaN(end.getTime())) return { start: null, end: null };
        return { start, end };
    }

    function setEndFromDate(end) {
        const yyyy = end.getFullYear();
        const mm2 = String(end.getMonth() + 1).padStart(2,'0');
        const dd = String(end.getDate()).padStart(2,'0');
        const hh2 = String(end.getHours()).padStart(2,'0');
        const mi2 = String(end.getMinutes()).padStart(2,'0');
        if (endDate) endDate.value = `${yyyy}-${mm2}-${dd}`;
        if (endTime) endTime.value = `${hh2}:${mi2}`;
    }

    function updateWorkedFromStartEnd() {
        if (!workedTime || suppressTimeSync) return;
        const { start, end } = getStartEnd();
        if (!start || !end) return;
        const diffMinutes = Math.round((end.getTime() - start.getTime()) / 60000);
        if (diffMinutes <= 0) return;
        suppressTimeSync = true;
        workedTime.value = formatWorkedMinutes(diffMinutes);
        // Auto-calculated; do not mark as explicit duration override.
        if (!workedTimeUserEdited && workedTimeMode) workedTimeMode.value = '';
        suppressTimeSync = false;
    }

    function updateEndFromStartWorked() {
        if (!workedTime || suppressTimeSync) return;
        if (!startDate || !startTime) return;
        if (!startDate.value || !startTime.value) return;
        const minutes = parseWorkedMinutes(workedTime.value);
        if (minutes == null) return;
        const start = new Date(`${startDate.value}T${startTime.value}:00`);
        if (isNaN(start.getTime())) return;
        const end = new Date(start.getTime() + minutes * 60000);
        suppressTimeSync = true;
        setEndFromDate(end);
        if (workedTimeMode) workedTimeMode.value = workedTimeUserEdited ? 'explicit' : '';
        suppressTimeSync = false;
    }

    // Suggest break from duration (default rules: >6h = 30 min, >9h = 45 min)
    const suggestBreakBtn = document.getElementById('suggestBreakBtn');
    const breakTimeInput = document.getElementById('break_time');
    if (suggestBreakBtn && breakTimeInput && form) {
        suggestBreakBtn.addEventListener('click', function() {
            let durationHours = 0;
            const workedMins = parseWorkedMinutes(workedTime && workedTime.value);
            if (workedMins != null) {
                durationHours = workedMins / 60;
            } else {
                const { start, end } = getStartEnd();
                if (start && end && end > start) {
                    durationHours = (end.getTime() - start.getTime()) / (60 * 60 * 1000);
                }
            }
            const h1 = parseFloat(form.dataset.breakAfterHours1 || '6');
            const m1 = parseInt(form.dataset.breakMinutes1 || '30', 10);
            const h2 = parseFloat(form.dataset.breakAfterHours2 || '9');
            const m2 = parseInt(form.dataset.breakMinutes2 || '45', 10);
            let suggestedMinutes = 0;
            if (durationHours >= h2) suggestedMinutes = m2;
            else if (durationHours >= h1) suggestedMinutes = m1;
            breakTimeInput.value = formatWorkedMinutes(suggestedMinutes);
        });
    }

    function onStartChange() {
        if (suppressTimeSync) return;
        // If worked time is valid, keep duration constant and move end; otherwise just recompute worked time.
        const minutes = workedTime ? parseWorkedMinutes(workedTime.value) : null;
        if (minutes != null) updateEndFromStartWorked();
        else updateWorkedFromStartEnd();
    }

    // Defer recalculation so the DOM has the latest date/time values (Issue #559)
    let scheduledWorkedTime = false;
    let pendingStart = false;
    let pendingEnd = false;
    function scheduleWorkedTimeUpdate(isStart) {
        if (isStart) pendingStart = true; else pendingEnd = true;
        if (scheduledWorkedTime) return;
        scheduledWorkedTime = true;
        queueMicrotask(function() {
            scheduledWorkedTime = false;
            const runStart = pendingStart;
            const runEnd = pendingEnd;
            pendingStart = false;
            pendingEnd = false;
            if (runStart) onStartChange();
            if (runEnd) updateWorkedFromStartEnd();
        });
    }

    // Dynamic task loading when a project is chosen
    const projectSelect = document.getElementById('project_id');
    const clientSelect = document.getElementById('client_id');
    const taskSelect = document.getElementById('task_id');
    const noTaskText = {{ _('No task')|tojson }};
    const failedToLoadTasksText = {{ _('Failed to load tasks')|tojson }};
    const scriptRoot = {{ request.script_root|default('')|tojson }};

    // Task loading: when project is selected, clear client (mutual exclusivity) and load tasks
    if (projectSelect && taskSelect) {
        projectSelect.addEventListener('change', function() {
            const projectId = this.value;
            if (clientSelect) clientSelect.value = '';
            loadTasks(projectId);
        });
    }

    // Client/project mutual exclusivity (when client select exists)
    if (clientSelect) {
        clientSelect.addEventListener('change', function() {
            if (this.value) {
                if (projectSelect) projectSelect.value = '';
                if (taskSelect) {
                    taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
                    taskSelect.disabled = true;
                }
            }
        });
    }
    
    function buildTasksUrl(projectId) {
        const pid = String(projectId || '').trim();
        if (!pid) return '';
        return (scriptRoot || '') + '/api/projects/' + encodeURIComponent(pid) + '/tasks';
    }

    async function loadTasks(projectId){
        if (!taskSelect) return;
        if (!projectId){
            taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
            taskSelect.disabled = true;
            return;
        }
        const url = buildTasksUrl(projectId);
        try{
            const resp = await fetch(url, {
                credentials: 'same-origin',
                headers: { 'Accept': 'application/json' },
                cache: 'no-store'
            });
            const ct = (resp.headers.get('content-type') || '').toLowerCase();
            const isJson = ct.includes('application/json');

            // Detect auth redirect: got HTML instead of JSON (session expired -> login page) (Issue #489)
            if (!isJson) {
                const sessionExpiredMsg = {{ _("Session may have expired. Please refresh the page and try again.")|tojson }};
                if (window.toastManager && typeof window.toastManager.error === 'function') {
                    window.toastManager.error(sessionExpiredMsg, {{ _("Error")|tojson }}, 6000);
                } else {
                    alert(sessionExpiredMsg);
                }
                taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
                taskSelect.disabled = true;
                return;
            }
            if (!resp.ok) {
                if (resp.status === 404) {
                    const projectNotFoundText = {{ _('Project not found or inactive')|tojson }};
                    if (window.toastManager && typeof window.toastManager.error === 'function') {
                        window.toastManager.error(projectNotFoundText, {{ _("Error")|tojson }}, 5000);
                    } else {
                        alert(projectNotFoundText);
                    }
                    taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
                    taskSelect.disabled = true;
                    return;
                }
                throw new Error(failedToLoadTasksText + ' (HTTP ' + resp.status + ')');
            }
            const data = await resp.json();
            const tasks = Array.isArray(data?.tasks) ? data.tasks : [];
            taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
            tasks.forEach(t => {
                const opt = document.createElement('option');
                opt.value = String(t.id);
                opt.textContent = t.name;
                taskSelect.appendChild(opt);
            });
            const pre = taskSelect.getAttribute('data-selected-task-id');
            if (pre){
                const found = Array.from(taskSelect.options).some(o => o.value === pre);
                if (found) taskSelect.value = pre;
                taskSelect.setAttribute('data-selected-task-id','');
            }
            taskSelect.disabled = false;
        }catch(e){
            taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
            taskSelect.disabled = true;
            try {
                const msg = e && e.message ? e.message : (failedToLoadTasksText || 'Failed to load tasks');
                if (window.toastManager && typeof window.toastManager.error === 'function') {
                    window.toastManager.error(msg, {{ _("Error")|tojson }}, 5000);
                } else if (window.toastManager && typeof window.toastManager.show === 'function') {
                    window.toastManager.show(msg, 'error');
                } else {
                    alert(msg);
                }
            } catch (_) {}
            if (typeof console !== 'undefined' && console.error) {
                console.error('[Log Time] Task dropdown failed. Project:', projectId, 'URL:', url, 'Error:', e);
            }
        }
    }
    
    if (projectSelect && projectSelect.value) {
        loadTasks(projectSelect.value);
    }

    // Keep worked time in sync with start/end inputs (date/time stay editable so user can combine duration + date)
    if (workedTime) {
        if (String(workedTime.value || '').trim()) {
            workedTimeUserEdited = true;
            setDurationOnlyMode(true);
        }

        updateWorkedFromStartEnd();

        workedTime.addEventListener('change', function() {
            workedTimeUserEdited = true;
            const mins = parseWorkedMinutes(workedTime.value);
            if (mins != null) setDurationOnlyMode(true);
            updateEndFromStartWorked();
        });
        workedTime.addEventListener('input', function() {
            if (!suppressTimeSync) workedTimeUserEdited = true;
            const raw = String(workedTime.value || '').trim();
            if (raw && workedTimeMode) workedTimeMode.value = 'explicit';
        });
        workedTime.addEventListener('blur', function() {
            // Normalize formatting if valid
            const mins = parseWorkedMinutes(workedTime.value);
            if (mins != null) workedTime.value = formatWorkedMinutes(mins);
        });
    }
    if (startDate) {
        startDate.addEventListener('change', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(true); });
        startDate.addEventListener('input', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(true); });
        startDate.addEventListener('focus', () => { if (startDate.disabled) { setDurationOnlyMode(false); ensureStartEndDefaultsIfEmpty(); } });
    }
    if (startTime) {
        startTime.addEventListener('change', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(true); });
        startTime.addEventListener('input', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(true); });
        startTime.addEventListener('focus', () => { if (startTime.disabled) { setDurationOnlyMode(false); ensureStartEndDefaultsIfEmpty(); } });
    }
    if (endDate) {
        endDate.addEventListener('change', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(false); });
        endDate.addEventListener('input', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(false); });
        endDate.addEventListener('focus', () => { if (endDate.disabled) { setDurationOnlyMode(false); ensureStartEndDefaultsIfEmpty(); } });
    }
    if (endTime) {
        endTime.addEventListener('change', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(false); });
        endTime.addEventListener('input', () => { timeFieldsUserEdited = true; scheduleWorkedTimeUpdate(false); });
        endTime.addEventListener('focus', () => { if (endTime.disabled) { setDurationOnlyMode(false); ensureStartEndDefaultsIfEmpty(); } });
    }
    
    // Form validation: ensure either project or client is selected and validate time range / duration-only
    // Use capture phase to run before other handlers
    const form = document.getElementById('manualEntryForm');
    if (form) {
        // Store original button state to restore if needed
        const submitBtn = form.querySelector('button[type="submit"]');
        let originalButtonHTML = submitBtn ? submitBtn.innerHTML : null;
        
        form.addEventListener('submit', function(e) {
            // Validate project or client selection
            const projectVal = projectSelect ? projectSelect.value : '';
            const clientVal = clientSelect ? clientSelect.value : '';
            if (!projectVal && !clientVal) {
                e.preventDefault();
                e.stopImmediatePropagation(); // Stop other handlers from running
                
                // Ensure button state is preserved
                if (submitBtn && originalButtonHTML) {
                    submitBtn.innerHTML = originalButtonHTML;
                    submitBtn.disabled = false;
                }
                
                        // Show error message using toast notification
                        const errorMsg = {{ _("Please select either a project or a client")|tojson }};
                        if (window.toastManager && typeof window.toastManager.error === 'function') {
                            window.toastManager.error(errorMsg, {{ _("Error")|tojson }}, 5000);
                        } else {
                            alert(errorMsg);
                        }
                        // After showing error, ensure button is still in correct state
                        if (submitBtn && originalButtonHTML) {
                            submitBtn.innerHTML = originalButtonHTML;
                            submitBtn.disabled = false;
                        }
                return false;
            }

            // Validate time entry requirements (task, description)
            const requireTask = form.dataset.requireTask === 'true';
            const requireDescription = form.dataset.requireDescription === 'true';
            const descMinLen = parseInt(form.dataset.descriptionMinLength || '20', 10);
            if (projectVal && requireTask) {
                const taskVal = taskSelect ? taskSelect.value : '';
                if (!taskVal) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    if (submitBtn && originalButtonHTML) {
                        submitBtn.innerHTML = originalButtonHTML;
                        submitBtn.disabled = false;
                    }
                    const errorMsg = {{ _("A task must be selected when logging time for a project")|tojson }};
                    if (window.toastManager && typeof window.toastManager.error === 'function') {
                        window.toastManager.error(errorMsg, {{ _("Error")|tojson }}, 5000);
                    } else {
                        alert(errorMsg);
                    }
                    return false;
                }
            }
            if (requireDescription) {
                const notesEl = document.getElementById('notes');
                let notesVal = notesEl ? notesEl.value : '';
                if (window.mdEditor && typeof window.mdEditor.getMarkdown === 'function') {
                    try { notesVal = window.mdEditor.getMarkdown(); } catch (err) {}
                }
                const notesTrimmed = (notesVal || '').trim();
                if (!notesTrimmed) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    if (submitBtn && originalButtonHTML) {
                        submitBtn.innerHTML = originalButtonHTML;
                        submitBtn.disabled = false;
                    }
                    const errorMsg = {{ _("A description is required when logging time")|tojson }};
                    if (window.toastManager && typeof window.toastManager.error === 'function') {
                        window.toastManager.error(errorMsg, {{ _("Error")|tojson }}, 5000);
                    } else {
                        alert(errorMsg);
                    }
                    return false;
                }
                if (notesTrimmed.length < descMinLen) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    if (submitBtn && originalButtonHTML) {
                        submitBtn.innerHTML = originalButtonHTML;
                        submitBtn.disabled = false;
                    }
                    const errorMsg = form.dataset.descriptionMinMsg || ('Description must be at least ' + descMinLen + ' characters');
                    if (window.toastManager && typeof window.toastManager.error === 'function') {
                        window.toastManager.error(errorMsg, {{ _("Error")|tojson }}, 5000);
                    } else {
                        alert(errorMsg);
                    }
                    return false;
                }
            }
            
            // Validate that either full start/end is present, or a worked_time duration is provided.
            const startDate = document.getElementById('start_date');
            const startTime = document.getElementById('start_time');
            const endDate = document.getElementById('end_date');
            const endTime = document.getElementById('end_time');
            const wt = document.getElementById('worked_time');
            const hasAllTimes = !!(startDate && startTime && endDate && endTime && startDate.value && startTime.value && endDate.value && endTime.value);
            const workedMinutes = wt ? parseWorkedMinutes(wt.value) : null;
            const hasWorked = workedMinutes != null;
            if (!hasAllTimes && !hasWorked) {
                e.preventDefault();
                e.stopImmediatePropagation();
                if (submitBtn && originalButtonHTML) {
                    submitBtn.innerHTML = originalButtonHTML;
                    submitBtn.disabled = false;
                }
                const errorMsg = {{ _("Please provide either start/end date+time or a worked time duration (HH:MM).")|tojson }};
                if (window.toastManager && typeof window.toastManager.error === 'function') {
                    window.toastManager.error(errorMsg, {{ _("Error")|tojson }}, 5000);
                } else {
                    alert(errorMsg);
                }
                return false;
            }
            
            // If we have start+end, validate the range. If duration-only, backend will compute range.
            if (hasAllTimes) {
                const start = new Date(`${startDate.value}T${startTime.value}:00`);
                const end = new Date(`${endDate.value}T${endTime.value}:00`);
                
                if (!isNaN(start.getTime()) && !isNaN(end.getTime())) {
                    if (end <= start) {
                        e.preventDefault();
                        e.stopImmediatePropagation(); // Stop other handlers from running
                        
                        // Ensure button state is preserved
                        if (submitBtn && originalButtonHTML) {
                            submitBtn.innerHTML = originalButtonHTML;
                            submitBtn.disabled = false;
                        }
                        
                        // Show error message using toast notification
                        const errorMsg = {{ _("End time must be after start time")|tojson }};
                        if (window.toastManager && typeof window.toastManager.error === 'function') {
                            window.toastManager.error(errorMsg, {{ _("Error")|tojson }}, 5000);
                        } else {
                            alert(errorMsg);
                        }
                        // After showing error, ensure button is still in correct state
                        if (submitBtn && originalButtonHTML) {
                            submitBtn.innerHTML = originalButtonHTML;
                            submitBtn.disabled = false;
                        }
                        return false;
                    }
                }
            }
        }, true); // Use capture phase to run before other handlers
    }

    // Apply Time Entry Template if provided via sessionStorage or query param
    // Skip template application when duplicating an entry to preserve the original entry's task
    const isDuplicating = {{ 'true' if is_duplicate else 'false' }};
    if (!isDuplicating) {
        try {
            let tpl = null;
            const raw = sessionStorage.getItem('activeTemplate');
            if (raw) {
                try { tpl = JSON.parse(raw); } catch(_) { tpl = null; }
            }
            if (!tpl) {
                const params = new URLSearchParams(window.location.search);
                const tplId = params.get('template');
                if (tplId) {
                    try {
                        const resp = await fetch(`/api/templates/${tplId}`);
                        if (resp.ok) tpl = await resp.json();
                    } catch(_) {}
                }
            }
            if (tpl && typeof tpl === 'object') {
                // Preselect project and task
                if (tpl.project_id && projectSelect) {
                    projectSelect.value = String(tpl.project_id);
                    if (clientSelect) clientSelect.value = '';
                    // Preselect task after load
                    if (taskSelect) {
                        taskSelect.setAttribute('data-selected-task-id', tpl.task_id ? String(tpl.task_id) : '');
                    }
                    await loadTasks(projectSelect.value);
                }

            // Notes, tags, billable
            const notes = document.getElementById('notes');
            const tagsInput = document.getElementById('tags');
            const billable = document.getElementById('billable');
            if (notes && tpl.default_notes) {
                notes.value = tpl.default_notes;
                // Update markdown editor if it exists
                if (window.mdEditor && typeof window.mdEditor.setMarkdown === 'function') {
                    try {
                        window.mdEditor.setMarkdown(tpl.default_notes);
                    } catch (e) {}
                }
            }
            if (tagsInput && tpl.tags) tagsInput.value = tpl.tags;
            if (billable != null && typeof tpl.billable === 'boolean') billable.checked = !!tpl.billable;

            // Duration → set end time relative to start if provided
            const minutes = (() => {
                if (typeof tpl.default_duration_minutes === 'number') return tpl.default_duration_minutes;
                if (typeof tpl.default_duration === 'number') return Math.round(tpl.default_duration * 60);
                return 0;
            })();
            if (minutes > 0) {
                const sd = document.getElementById('start_date');
                const st = document.getElementById('start_time');
                const ed = document.getElementById('end_date');
                const et = document.getElementById('end_time');
                if (sd && st && ed && et && sd.value && st.value) {
                    const start = new Date(`${sd.value}T${st.value}:00`);
                    if (!isNaN(start.getTime())) {
                        const end = new Date(start.getTime() + minutes * 60000);
                        const yyyy = end.getFullYear();
                        const mm = String(end.getMonth() + 1).padStart(2,'0');
                        const dd = String(end.getDate()).padStart(2,'0');
                        const hh = String(end.getHours()).padStart(2,'0');
                        const mi = String(end.getMinutes()).padStart(2,'0');
                        ed.value = `${yyyy}-${mm}-${dd}`;
                        et.value = `${hh}:${mi}`;
                    }
                }
            }
            // Update worked time field after applying duration, if present
            try { updateWorkedFromStartEnd(); } catch (_) {}

                // Clear after applying so it does not persist
                try { sessionStorage.removeItem('activeTemplate'); } catch(_) {}
            }
        } catch(_) {}
    }
});
</script>

<!-- Toast UI Editor: load only on desktop to avoid mobile freeze (Issue #557) -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    if (window.isMobileView) {
        window.mdEditor = null;
        return;
    }
    var link1 = document.createElement('link');
    link1.rel = 'stylesheet';
    link1.href = 'https://uicdn.toast.com/editor/latest/toastui-editor.min.css';
    document.head.appendChild(link1);
    var link2 = document.createElement('link');
    link2.rel = 'stylesheet';
    link2.href = 'https://uicdn.toast.com/editor/latest/theme/toastui-editor-dark.css';
    document.head.appendChild(link2);
    var scriptEl = document.createElement('script');
    scriptEl.src = 'https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js';
    scriptEl.onload = function initToastEditorManualEntry() {
    const notesInput = document.getElementById('notes');
    let mdEditor = null;

    if (notesInput && window.toastui && window.toastui.Editor) {
        const theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
        mdEditor = new toastui.Editor({
            el: document.getElementById('notes_editor'),
            height: '300px',
            initialEditType: 'wysiwyg',
            previewStyle: 'vertical',
            usageStatistics: false,
            hideModeSwitch: false,
            placeholder: {{ _("What did you work on?")|tojson }},
            theme: theme,
            toolbarItems: [
                ['heading', 'bold', 'italic', 'strike'],
                ['hr', 'quote'],
                ['ul', 'ol', 'task'],
                ['link', 'code', 'codeblock', 'table'],
                ['image'],
                ['scrollSync']
            ],
            initialValue: notesInput.value || ''
        });

        // Apply theme changes dynamically if supported
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mdEditor) {
                    const nextTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
                    try {
                        if (typeof mdEditor.setTheme === 'function') {
                            mdEditor.setTheme(nextTheme);
                        }
                    } catch (e) {}
                }
            });
        });
        observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });

        // Multiple image upload handler - improved version
        mdEditor.removeHook && mdEditor.removeHook('addImageBlobHook');
        
        // Create custom multiple file input with unique ID
        const fileInputId = 'manual-entry-image-input-' + Date.now();
        let fileInput = document.getElementById(fileInputId);
        if (!fileInput) {
            fileInput = document.createElement('input');
            fileInput.id = fileInputId;
            fileInput.type = 'file';
            fileInput.accept = 'image/*';
            fileInput.setAttribute('multiple', 'multiple'); // Use setAttribute to ensure it's set
            fileInput.style.display = 'none';
            fileInput.style.position = 'absolute';
            fileInput.style.left = '-9999px';
            document.body.appendChild(fileInput);
        }
        
        // Verify multiple attribute is set
        if (!fileInput.hasAttribute('multiple')) {
            fileInput.setAttribute('multiple', 'multiple');
        }
        console.log('File input multiple attribute:', fileInput.multiple, fileInput.hasAttribute('multiple'));
        
        // Function to find image button with multiple strategies
        function findImageButton(toolbar, retries = 0) {
            if (!toolbar) return null;
            
            // Try multiple selectors in order of reliability
            const selectors = [
                '[data-tooltip="Insert image"]',
                '[data-tooltip*="image" i]',
                '[aria-label*="image" i]',
                '.toastui-editor-toolbar-icons.image',
                'button[class*="image"]',
                '.toastui-editor-toolbar-group button:nth-child(5)', // Common position for image button
                '.toastui-editor-toolbar-group button:nth-child(6)',
                '.toastui-editor-toolbar-group button:nth-child(4)'
            ];
            
            for (const selector of selectors) {
                try {
                    const button = toolbar.querySelector(selector);
                    if (button) {
                        // Verify it's likely the image button by checking if it has image-related attributes or classes
                        const buttonText = (button.textContent || '').toLowerCase();
                        const buttonTitle = (button.title || button.getAttribute('title') || '').toLowerCase();
                        if (buttonText.includes('image') || buttonTitle.includes('image') || 
                            selector.includes('image') || button.classList.toString().includes('image')) {
                            return button;
                        }
                    }
                } catch (e) {
                    // Continue to next selector
                }
            }
            
            return null;
        }
        
        // Function to intercept image button with retry logic
        function setupImageButtonInterception(editor, input, maxRetries = 5) {
            let attempts = 0;
            let intercepted = false;
            
            function tryIntercept() {
                attempts++;
                try {
                    // Get the editor's root element - try multiple ways
                    const editorContainer = document.getElementById('notes_editor');
                    if (!editorContainer) {
                        throw new Error('Editor container element not found');
                    }
                    
                    // Find toolbar - try multiple ways to access it
                    let toolbar = null;
                    // Try finding toolbar relative to editor container
                    let editorWrapper = null;
                    if (editorContainer.closest) {
                        editorWrapper = editorContainer.closest('.toastui-editor');
                    }
                    if (!editorWrapper && editorContainer.parentElement) {
                        editorWrapper = editorContainer.parentElement;
                    }
                    if (editorWrapper) {
                        toolbar = editorWrapper.querySelector('.toastui-editor-toolbar');
                    }
                    // Fallback: search entire document
                    if (!toolbar) {
                        toolbar = document.querySelector('.toastui-editor-toolbar');
                    }
                    
                    if (toolbar && !intercepted) {
                        const imageButton = findImageButton(toolbar, attempts);
                        if (imageButton) {
                            // Use capture phase to intercept before ToastUI's handler
                            imageButton.addEventListener('click', function handler(e) {
                                e.preventDefault();
                                e.stopPropagation();
                                e.stopImmediatePropagation();
                                
                                // Small delay to ensure default behavior is prevented
                                setTimeout(() => {
                                    input.click();
                                }, 10);
                                
                                return false;
                            }, true); // Use capture phase
                            
                            intercepted = true;
                            console.log('Successfully intercepted image button for multiple file selection');
                            return true;
                        }
                    }
                } catch (err) {
                    console.warn('Error intercepting image button (attempt ' + attempts + '):', err);
                }
                
                // Retry if we haven't exceeded max retries
                if (attempts < maxRetries && !intercepted) {
                    setTimeout(tryIntercept, 200 * attempts); // Exponential backoff
                    return false;
                } else if (!intercepted) {
                    console.warn('Could not intercept image button after ' + maxRetries + ' attempts. Multiple image selection may not work via toolbar button.');
                    return false;
                }
                return false;
            }
            
            // Start with initial delay to ensure toolbar is ready
            setTimeout(tryIntercept, 100);
        }
        
        // Override the addImageBlobHook to use multiple file selection
        // This is the fallback if button interception fails
        if (mdEditor && typeof mdEditor.addHook === 'function') {
            mdEditor.addHook('addImageBlobHook', async function(blob, callback) {
                // Prevent the default single file behavior
                // Trigger our multiple file input instead
                setTimeout(function() {
                    fileInput.click();
                }, 10);
                // Don't call callback - let fileInput.change handle it
            });
        }
        
        // Setup button interception
        if (mdEditor) {
            setupImageButtonInterception(mdEditor, fileInput);
        }
        
        // Handle multiple file selection with progress feedback
        fileInput.addEventListener('change', async (e) => {
            const files = Array.from(e.target.files);
            console.log('File input changed. Files selected:', files.length, 'Multiple attribute:', fileInput.multiple);
            
            if (files.length === 0) {
                console.log('No files selected');
                return;
            }
            
            // Log file details for debugging
            files.forEach((file, index) => {
                console.log(`File ${index + 1}: ${file.name}, type: ${file.type}, size: ${file.size}`);
            });
            
            // Validate file types
            const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'];
            const validFiles = files.filter(file => {
                const isValid = allowedTypes.includes(file.type) || /\.(png|jpg|jpeg|gif|webp)$/i.test(file.name);
                if (!isValid) {
                    console.warn('Skipping invalid file type:', file.name, file.type);
                }
                return isValid;
            });
            
            if (validFiles.length === 0) {
                alert({{ _("No valid image files selected. Please select PNG, JPG, GIF, or WebP images.")|tojson }});
                fileInput.value = '';
                return;
            }
            
            if (validFiles.length < files.length) {
                console.warn('Some files were skipped due to invalid type');
            }
            
            // Show loading feedback
            const fileCount = validFiles.length;
            const loadingMsg = fileCount > 1 ? 
                        ({{ _("Uploading")|tojson }} + ' ' + fileCount + ' ' + {{ _("images")|tojson }} + '...') :
                {{ _("Uploading image")|tojson }} + '...';
            
            // Try to show a temporary loading indicator (if toast/notification system exists)
            let loadingIndicator = null;
            try {
                // Check if there's a toast/notification system we can use
                if (window.showToast || window.showNotification) {
                    const showToast = window.showToast || window.showNotification;
                    loadingIndicator = showToast(loadingMsg, 'info', { duration: 0 });
                }
            } catch (e) {
                // No toast system available, continue without it
            }
            
            try {
                const formData = new FormData();
                validFiles.forEach(file => {
                    formData.append('images', file);
                });
                
                const res = await fetch('{{ url_for('api.upload_editor_images_bulk') }}', {
                    method: 'POST',
                    body: formData
                });
                
                const data = await res.json();
                if (data && data.urls && data.urls.length > 0) {
                    // Insert all images into editor
                    const imagesMarkdown = data.urls.map((url, index) => {
                        const fileName = validFiles[index].name || 'image';
                        return `![${fileName}](${url})`;
                    }).join('\n\n');
                    
                    // Get current markdown content
                    const currentMarkdown = mdEditor.getMarkdown() || '';
                    
                    // Append images to current content (with proper spacing)
                    const newMarkdown = currentMarkdown 
                        ? currentMarkdown + '\n\n' + imagesMarkdown
                        : imagesMarkdown;
                    
                    // Set the markdown which will properly render images in WYSIWYG mode
                    mdEditor.setMarkdown(newMarkdown);
                    
                    // Show success message
                    const successMsg = data.urls.length > 1 ? 
                        ({{ _("Successfully uploaded")|tojson }} + ' ' + data.urls.length + ' ' + {{ _("images")|tojson }}) :
                        {{ _("Image uploaded successfully")|tojson }};
                    
                    if (loadingIndicator && typeof loadingIndicator.update === 'function') {
                        loadingIndicator.update(successMsg, 'success');
                        setTimeout(() => loadingIndicator.remove && loadingIndicator.remove(), 2000);
                    }
                    
                    // Show warnings if any
                    if (data.warnings && data.warnings.length > 0) {
                        console.warn('Some images failed to upload:', data.warnings);
                        const warningMsg = {{ _("Warning")|tojson }} + ': ' + data.warnings.length + ' ' + {{ _("image(s) failed to upload")|tojson }};
                        if (window.showToast || window.showNotification) {
                            const showToast = window.showToast || window.showNotification;
                            showToast(warningMsg, 'warning');
                        } else {
                            alert(warningMsg);
                        }
                    }
                } else {
                    const errorMsg = data.error || {{ _("Failed to upload images. Please try again.")|tojson }};
                    console.error('Upload failed', data);
                    if (loadingIndicator && typeof loadingIndicator.remove === 'function') {
                        loadingIndicator.remove();
                    }
                    alert(errorMsg);
                }
            } catch (error) {
                console.error('Multiple image upload error', error);
                if (loadingIndicator && typeof loadingIndicator.remove === 'function') {
                    loadingIndicator.remove();
                }
                alert({{ _("Failed to upload images. Please check your connection and try again.")|tojson }});
            }
            
            // Reset file input
            fileInput.value = '';
        });

        // Make editor accessible globally for template application
        window.mdEditor = mdEditor;

        // Sync editor content to hidden textarea on form submit
        const form = document.getElementById('manualEntryForm');
        if (form) {
            form.addEventListener('submit', function(e) {
                if (mdEditor && notesInput) {
                    try {
                        notesInput.value = mdEditor.getMarkdown();
                    } catch (err) {
                        console.error('Failed to sync markdown editor:', err);
                    }
                }
            });
        }

        // Update notes when template is applied
        const originalNotesHandler = window.addEventListener;
        // This will be handled in the existing template application code
    }
    }; // end Toast UI init onload
    document.head.appendChild(scriptEl);
});
</script>
{% endblock %}
