<?= $this->extend("layout/main_layout"); ?>

<?= $this->section("content"); ?>
<style>
    /* Hide number input spinners */
    .no-spinner::-webkit-outer-spin-button,
    .no-spinner::-webkit-inner-spin-button {
        -webkit-appearance: none;
        margin: 0;
    }
    .no-spinner {
        -moz-appearance: textfield;
    }
</style>
<main class="flex-1 p-6 overflow-auto" x-data="monthlyEntry()">
    <div class="flex justify-between items-center mb-6">
        <div>
            <h1 class="text-2xl font-bold text-base-content tracking-tight">Monthly Entry</h1>
            <p class="text-sm mt-1 opacity-70">Batch enter monthly results for all control levels</p>
        </div>
        <div class="flex gap-2">
            <button @click="resetData()" class="btn btn-ghost btn-sm" x-show="hasChanges">
                Discard Changes
            </button>
            <button @click="saveAll()" :disabled="saving || !canSave" class="btn btn-primary"
                :class="{ 'loading': saving }">
                <i class="fa-solid fa-save mr-2"></i>
                Save Results
            </button>
        </div>
    </div>

    <!-- Filters -->
    <div class="bg-base-100 rounded-xl border border-base-300 shadow-sm p-4 mb-6">
        <div class="flex flex-wrap gap-6 items-center">
            <div class="form-control">
                <label class="label pt-0">
                    <span class="label-text font-semibold opacity-60 uppercase text-[10px]">Reporting Month</span>
                </label>
                <div class="join">
                    <button @click="prevMonth()" class="join-item btn btn-sm btn-outline border-base-300"><i
                            class="fa-solid fa-chevron-left"></i></button>
                    <input type="month" x-model="month" @change="onMonthChange()"
                        class="join-item input input-bordered input-sm w-36 text-center font-medium bg-base-200/30">
                    <button @click="nextMonth()" class="join-item btn btn-sm btn-outline border-base-300"><i
                            class="fa-solid fa-chevron-right"></i></button>
                </div>
            </div>

            <div class="divider divider-horizontal mx-0 hidden sm:flex"></div>

            <div class="form-control">
                <label class="label pt-0">
                    <span class="label-text font-semibold opacity-60 uppercase text-[10px]">Department</span>
                </label>
                <select x-model="deptId" @change="setDeptId(deptId)" class="select select-bordered select-sm w-48">
                    <option value="">All Departments</option>
                    <template x-if="departments">
                        <template x-for="dept in departments" :key="dept.deptId">
                            <option :value="dept.deptId" x-text="dept.deptName"></option>
                        </template>
                    </template>
                    <template x-if="!departments">
                        <option disabled>Loading...</option>
                    </template>
                </select>
            </div>

            <div class="divider divider-horizontal mx-0 hidden sm:flex"></div>

            <div class="form-control flex-1 max-w-xs">
                <label class="label pt-0">
                    <span class="label-text font-semibold opacity-60 uppercase text-[10px]">Select Test</span>
                </label>
                <select x-model="selectedTest" @change="fetchMonthlyData()"
                    class="select select-bordered select-sm w-full font-medium">
                    <option value="">Choose a test...</option>
                    <template x-for="test in tests" :key="test.testId">
                        <option :value="String(test.testId)" x-text="test.testName"></option>
                    </template>
                </select>
            </div>

            <template x-if="selectedTest">
                <div class="flex flex-col">
                    <span class="text-[10px] font-semibold opacity-60 uppercase mb-1">Unit</span>
                    <span class="badge badge-outline badge-sm" x-text="testUnit || 'N/A'"></span>
                </div>
            </template>
        </div>
    </div>

    <!-- Loading State -->
    <div x-show="loading" class="flex flex-col items-center justify-center py-24 gap-4">
        <span class="loading loading-spinner loading-lg text-primary"></span>
        <p class="text-sm opacity-50 animate-pulse">Fetching records...</p>
    </div>

    <!-- Empty/No Selection State -->
    <div x-show="!loading && (!selectedTest || (selectedTest && controls.length === 0))"
        class="bg-base-100 rounded-2xl border-2 border-dashed border-base-300 p-16 text-center">
        <div class="w-16 h-16 bg-base-200 rounded-full flex items-center justify-center mx-auto mb-4">
            <i class="fa-solid"
                :class="!selectedTest ? 'fa-flask-vial text-2xl opacity-20' : 'fa-triangle-exclamation text-2xl text-warning'"></i>
        </div>
        <template x-if="!selectedTest">
            <div>
                <h3 class="font-bold text-lg">No Test Selected</h3>
                <p class="text-base-content/60 max-w-xs mx-auto">Please select a laboratory test from the dropdown above
                    to begin data entry.</p>
            </div>
        </template>
        <template x-if="selectedTest && controls.length === 0">
            <div>
                <h3 class="font-bold text-lg">No Controls Found</h3>
                <p class="text-base-content/60 max-w-xs mx-auto">This test doesn't have any controls configured for the
                    selected period.</p>
            </div>
        </template>
    </div>

    <!-- Calendar Grid -->
    <div x-show="!loading && selectedTest && controls.length > 0" class="space-y-6">

        <div class="bg-base-100 rounded-xl border border-base-300 shadow-sm overflow-hidden overflow-x-auto relative">
            <table class="table-auto w-full border-collapse">
                <thead>
                    <tr class="bg-base-200 border-b border-base-300">
                        <th
                            class="sticky left-0 bg-base-200 z-20 w-16 p-3 text-center font-bold text-xs uppercase tracking-wider border-r border-base-300">
                            Day
                        </th>
                        <template x-for="(control, cIdx) in controls" :key="control.controlId">
                            <th class="p-3 text-center border-r border-base-300 min-w-32 bg-base-200/50">
                                <div class="flex flex-col gap-1">
                                    <span class="text-sm font-bold truncate" x-text="control.controlName"></span>
                                    <span class="text-[10px] opacity-60 font-mono"
                                        x-text="'LOT: ' + (control.lot || 'N/A')"></span>
                                    <div class="flex items-center justify-center gap-1 mt-1">
                                        <span class="badge badge-xs badge-neutral px-1.5"
                                            x-text="formatParam(control)"></span>
                                    </div>
                                </div>
                            </th>
                        </template>
                    </tr>
                </thead>
                <tbody @keydown="handleKeydown($event)">
                    <template x-for="day in daysInMonth" :key="day">
                        <tr class="hover:bg-primary/5 transition-colors group">
                            <td class="sticky left-0 z-10 p-2 text-center font-mono font-bold text-sm bg-base-100 border-r border-base-300 group-hover:bg-primary/10"
                                :class="{
                                    'bg-base-200/50 text-base-content/40': isWeekend(day),
                                    'text-primary bg-primary/5': isToday(day)
                                }">
                                <span x-text="day"></span>
                                <span class="block text-[8px] opacity-40 leading-none mt-0.5"
                                    x-text="getDayName(day)"></span>
                            </td>
                            <template x-for="(control, cIdx) in controls" :key="control.controlId">
                                <td class="p-1 border-r border-base-200 last:border-r-0"
                                    :class="{ 'bg-base-200/20': isWeekend(day) }">
                                    <div class="relative group/cell px-0.5">
                                        <input type="number" step="any"
                                            class="input input-sm input-ghost no-spinner w-full text-center font-mono text-sm rounded focus:bg-base-100 focus:shadow-sm focus:ring-1 focus:ring-primary/20 placeholder:text-base-content/20 transition-all"
                                            :class="getCellClass(control, day)" :data-day="day" :data-cidx="cIdx"
                                            x-model="resultsData[control.controlId + '_' + day]"
                                            @focus="onCellFocus(control, day, $event)" placeholder="-">

                                        <!-- Floating Action Buttons for cell -->
                                        <div
                                            class="absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-1 opacity-0 group-hover/cell:opacity-100 transition-opacity pointer-events-none">
                                            <button @click="showComment(control.controlId, day)"
                                                class="btn btn-circle btn-ghost btn-xs h-5 w-5 min-h-0 pointer-events-auto"
                                                :class="hasComment(control.controlId, day) ? 'text-info' : 'text-base-content/30'">
                                                <i class="fa-solid fa-comment text-[10px]"></i>
                                            </button>
                                        </div>

                                        <!-- Highlight indicator for edited cells -->
                                        <div x-show="isChanged(control.controlId, day)"
                                            class="absolute left-1 top-1.5 w-1.5 h-1.5 rounded-full bg-info ring-1 ring-base-100 shadow-sm pointer-events-none">
                                        </div>
                                    </div>
                                </td>
                            </template>
                        </tr>
                    </template>
                </tbody>
            </table>
        </div>

        <!-- Legend & Footer Info -->
        <div class="flex flex-wrap items-center justify-between gap-4 px-2">
            <div class="flex flex-wrap gap-4 text-[10px] items-center">
                <div class="flex items-center gap-1.5 opacity-70">
                    <span class="w-3 h-3 rounded bg-success/20 border border-success/30"></span>
                    <span class="font-medium">In Range (± 2SD)</span>
                </div>
                <div class="flex items-center gap-1.5 opacity-70">
                    <span class="w-3 h-3 rounded bg-error/20 border border-error/30"></span>
                    <span class="font-medium">Out of Range</span>
                </div>
                <div class="flex items-center gap-1.5 opacity-70">
                    <span class="w-3 h-3 rounded bg-base-200 border border-base-300"></span>
                    <span class="font-medium">Weekend</span>
                </div>
                <div class="flex items-center gap-1.5 opacity-70">
                    <div class="w-1.5 h-1.5 rounded-full bg-info"></div>
                    <span class="font-medium">Unsaved Change</span>
                </div>
            </div>

            <div class="flex items-center gap-3 text-xs opacity-60 italic">
                <i class="fa-solid fa-lightbulb"></i>
                <span>Tip: Use arrow keys to navigate between cells.</span>
            </div>
        </div>
    </div>

    <!-- Summary Float -->
    <div x-show="hasChanges" x-transition:enter="transition ease-out duration-300"
        x-transition:enter-start="translate-y-full opacity-0" x-transition:enter-end="translate-y-0 opacity-100"
        class="fixed bottom-6 right-6 z-40">
        <div
            class="bg-info text-info-content px-4 py-3 rounded-2xl shadow-2xl flex items-center gap-4 border border-info-content/20">
            <div class="flex items-center gap-2">
                <i class="fa-solid fa-circle-exclamation animate-pulse"></i>
                <span class="font-bold"><span x-text="changedCount"></span> unsaved entries</span>
            </div>
            <div class="divider divider-horizontal mx-0 bg-info-content/20 w-[1px] h-4"></div>
            <button @click="saveAll()"
                class="btn btn-sm btn-ghost bg-white/10 hover:bg-white/20 border-none text-info-content">
                Save Now (Ctrl+S)
            </button>
        </div>
    </div>

    <!-- Comment Modal -->
    <div x-show="commentModal.show" x-transition.opacity
        class="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-[100]"
        @click.self="commentModal.show = false">
        <div class="bg-base-100 rounded-2xl shadow-2xl w-full max-w-md p-6 m-4 overflow-hidden border border-base-300"
            x-transition:enter="transition ease-out duration-200" x-transition:enter-start="scale-95 opacity-0"
            x-transition:enter-end="scale-100 opacity-100">
            <div class="flex items-center gap-3 mb-4">
                <div class="w-10 h-10 rounded-xl bg-info/10 text-info flex items-center justify-center">
                    <i class="fa-solid fa-comment-dots text-lg"></i>
                </div>
                <div>
                    <h3 class="font-bold text-lg">Daily Comment</h3>
                    <p class="text-xs opacity-50 uppercase font-semibold tracking-wider"
                        x-text="commentModal.dateDisplay"></p>
                </div>
            </div>

            <textarea x-model="commentModal.text"
                class="textarea textarea-bordered w-full min-h-32 text-sm focus:ring-1 focus:ring-primary"
                placeholder="Enter remark or observation code..."></textarea>

            <div class="flex justify-between items-center mt-6">
                <button @click="clearComment()" class="btn btn-sm btn-ghost text-error hover:bg-error/5">
                    Clear Remark
                </button>
                <div class="flex gap-2">
                    <button @click="commentModal.show = false" class="btn btn-sm btn-ghost font-bold">Discard</button>
                    <button @click="saveComment()" class="btn btn-sm btn-primary px-6">Apply</button>
                </div>
            </div>
        </div>
    </div>
</main>
<?= $this->endSection(); ?>

<?= $this->section("script"); ?>
<script>
document.addEventListener('alpine:init', () => {
    Alpine.data("monthlyEntry", () => ({
        tests: [],
        selectedTest: null,
        controls: [],
        loading: false,
        saving: false,
        resultsData: {},
        originalResults: {}, // To track changes
        commentsData: {},
        originalComments: {},
        month: '',
        deptId: null,
        departments: null,
        commentModal: {
            show: false,
            controlId: null,
            day: null,
            dateDisplay: '',
            text: ''
        },

        init() {
            const now = new Date();
            this.month = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
            this.fetchDepartments();
            this.fetchTests();
            this.setupKeyboard();
        },

        async fetchDepartments() {
            try {
                const response = await fetch(`${BASEURL}api/master/depts`, {
                    method: "GET",
                    headers: { "Content-Type": "application/json" }
                });
                if (!response.ok) throw new Error("Failed to load departments");
                const json = await response.json();
                this.departments = json.data || [];
            } catch (e) {
                console.error('Failed to fetch departments:', e);
            }
        },

        async fetchTests() {
            try {
                const params = new URLSearchParams();
                if (this.deptId) {
                    params.set('dept_id', this.deptId);
                }
                const url = `${BASEURL}api/master/tests${this.deptId ? '?' + params.toString() : ''}`;
                const response = await fetch(url);
                const json = await response.json();
                this.tests = json.data || [];
            } catch (e) {
                console.error('Failed to fetch tests:', e);
            }
        },

        
        setDeptId(id) {
            this.deptId = id;
            this.selectedTest = null;
            this.controls = [];
            this.fetchTests();
        },

        onMonthChange() {
            if (this.selectedTest) {
                this.fetchMonthlyData();
            }
        },

        async fetchMonthlyData() {
            if (!this.selectedTest) {
                this.controls = [];
                return;
            }

            this.loading = true;
            try {
                const params = new URLSearchParams({
                    test_id: this.selectedTest,
                    month: this.month
                });
                const response = await fetch(`${BASEURL}api/entry/monthly?${params}`);
                const json = await response.json();

                if (json.status === 'success') {
                    this.controls = json.data.controls || [];

                    // Build results and comments lookup
                    this.resultsData = {};
                    this.originalResults = {};
                    this.commentsData = {};
                    this.originalComments = {};

                    for (const control of this.controls) {
                        for (let day = 1; day <= 31; day++) {
                            const result = control.results[day];
                            const key = `${control.controlId}_${day}`;
                            if (result) {
                                if (result.resValue !== null) {
                                    this.resultsData[key] = result.resValue;
                                    this.originalResults[key] = result.resValue;
                                }
                                if (result.resComment) {
                                    this.commentsData[key] = result.resComment;
                                    this.originalComments[key] = result.resComment;
                                }
                            }
                        }
                    }
                }
            } catch (e) {
                console.error('Failed to fetch monthly data:', e);
                this.$dispatch('notify', { type: 'error', message: 'Network error while fetching data' });
            } finally {
                this.loading = false;
            }
        },

        async saveAll() {
            if (!this.canSave) return;

            this.saving = true;
            try {
                const controlsToSave = [];
                for (const control of this.controls) {
                    const results = {};
                    let hasControlData = false;

                    for (let day = 1; day <= 31; day++) {
                        const key = `${control.controlId}_${day}`;
                        const value = this.resultsData[key];
                        const comment = this.commentsData[key];

                        // Only save if it's different from original OR it's a new non-empty value
                        if (this.isChanged(control.controlId, day)) {
                            results[day] = {
                                value: value === '' ? null : value,
                                comment: comment || null
                            };
                            hasControlData = true;
                        }
                    }

                    if (hasControlData) {
                        controlsToSave.push({
                            controlId: control.controlId,
                            results: results
                        });
                    }
                }

                if (controlsToSave.length === 0) {
                    this.saving = false;
                    return;
                }

                const response = await fetch(`${BASEURL}api/entry/monthly`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        testId: this.selectedTest,
                        month: this.month,
                        controls: controlsToSave
                    })
                });

                const json = await response.json();
                if (json.status === 'success') {
                    // Save comments for changed entries
                    for (const key in this.commentsData) {
                        if (this.originalComments[key] !== this.commentsData[key]) {
                            const [controlId, day] = key.split('_');
                            const date = `${this.month}-${day.padStart(2, '0')}`;
                            await this.saveComment(this.selectedTest, date, this.commentsData[key]);
                        }
                    }

                    this.$dispatch('notify', { type: 'success', message: json.message });
                    await this.fetchMonthlyData();
                } else {
                    this.$dispatch('notify', { type: 'error', message: json.message || 'Failed to save' });
                }
            } catch (e) {
                console.error('Failed to save:', e);
                this.$dispatch('notify', { type: 'error', message: 'Failed to save results' });
            } finally {
                this.saving = false;
            }
        },

        async saveComment(testId, date, commentText) {
            try {
                const response = await fetch(`${BASEURL}api/entry/comment`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        testId: testId,
                        date: date,
                        comment: commentText
                    })
                });
                return response.json();
            } catch (e) {
                console.error('Failed to save comment:', e);
                return { status: 'error', message: e.message };
            }
        },

        isChanged(controlId, day) {
            const key = `${controlId}_${day}`;
            const currentVal = this.resultsData[key] === undefined ? '' : String(this.resultsData[key]);
            const originalVal = this.originalResults[key] === undefined ? '' : String(this.originalResults[key]);
            
            const currentCom = this.commentsData[key] || '';
            const originalCom = this.originalComments[key] || '';

            return currentVal !== originalVal || currentCom !== originalCom;
        },

        resetData() {
            if (confirm('Discard all unsaved changes for this month?')) {
                this.resultsData = JSON.parse(JSON.stringify(this.originalResults));
                this.commentsData = JSON.parse(JSON.stringify(this.originalComments));
            }
        },

        // Comment modal methods
        showComment(controlId, day) {
            const [year, month] = this.month.split('-').map(Number);
            const date = new Date(year, month - 1, day);
            const dateStr = date.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' });

            this.commentModal = {
                show: true,
                controlId: controlId,
                day: day,
                dateDisplay: dateStr,
                text: this.commentsData[`${controlId}_${day}`] || ''
            };
        },

        saveComment() {
            const key = `${this.commentModal.controlId}_${this.commentModal.day}`;
            if (this.commentModal.text.trim() === '') {
                delete this.commentsData[key];
            } else {
                this.commentsData[key] = this.commentModal.text.trim();
            }
            this.commentModal.show = false;
        },

        clearComment() {
            this.commentModal.text = '';
            this.saveComment();
        },

        hasComment(controlId, day) {
            return !!this.commentsData[`${controlId}_${day}`];
        },

        getCellClass(control, day) {
            const key = `${control.controlId}_${day}`;
            const value = this.resultsData[key];
            if (value === undefined || value === '') return '';

            if (control.mean === null || control.sd === null) return '';

            const num = parseFloat(value);
            const lower = control.mean - 2 * control.sd;
            const upper = control.mean + 2 * control.sd;

            if (num >= lower && num <= upper) return 'text-success bg-success/5 font-bold';
            return 'text-error bg-error/5 font-bold';
        },

        formatParam(control) {
            if (control.mean === null || control.sd === null) return 'No target';
            return parseFloat(control.mean).toFixed(2) + ' ± ' + (2 * control.sd).toFixed(2);
        },

        getDayName(day) {
            const [year, month] = this.month.split('-').map(Number);
            const date = new Date(year, month - 1, day);
            return date.toLocaleDateString('en-US', { weekday: 'short' });
        },

        isToday(day) {
            const now = new Date();
            const [year, month] = this.month.split('-').map(Number);
            return now.getDate() === day && (now.getMonth() + 1) === month && now.getFullYear() === year;
        },

        get daysInMonth() {
            if (!this.month) return [];
            const [year, month] = this.month.split('-').map(Number);
            const days = new Date(year, month, 0).getDate();
            return Array.from({ length: days }, (_, i) => i + 1);
        },

        get monthDisplay() {
            if (!this.month) return '';
            const [year, month] = this.month.split('-');
            const date = new Date(year, month - 1);
            return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
        },

        get testUnit() {
            const test = this.tests.find(t => t.id == this.selectedTest);
            return test ? test.testUnit : '';
        },

        prevMonth() {
            const [year, month] = this.month.split('-').map(Number);
            const date = new Date(year, month - 2, 1);
            this.month = date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0');
            this.onMonthChange();
        },

        nextMonth() {
            const [year, month] = this.month.split('-').map(Number);
            const date = new Date(year, month, 1);
            this.month = date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0');
            this.onMonthChange();
        },

        isWeekend(day) {
            const [year, month] = this.month.split('-').map(Number);
            const date = new Date(year, month - 1, day);
            const dayOfWeek = date.getDay();
            return dayOfWeek === 0 || dayOfWeek === 6;
        },

        get hasChanges() {
            // Check resultsData vs originalResults
            for (const key in this.resultsData) {
                const day = parseInt(key.split('_')[1]);
                const controlId = parseInt(key.split('_')[0]);
                if (this.isChanged(controlId, day)) return true;
            }
            // Check if any in original are now empty
            for (const key in this.originalResults) {
                if (this.resultsData[key] === undefined || this.resultsData[key] === '') return true;
            }
            // Check comments
            for (const key in this.commentsData) {
                const day = parseInt(key.split('_')[1]);
                const controlId = parseInt(key.split('_')[0]);
                if (this.isChanged(controlId, day)) return true;
            }
            return false;
        },

        get changedCount() {
            let count = 0;
            // Collect all unique keys from both current and original
            const keys = new Set([...Object.keys(this.resultsData), ...Object.keys(this.originalResults), ...Object.keys(this.commentsData)]);
            for (const key of keys) {
                const parts = key.split('_');
                if (this.isChanged(parts[0], parts[1])) count++;
            }
            return count;
        },

        get canSave() {
            return this.selectedTest && this.hasChanges && !this.saving;
        },

        onCellFocus(control, day, event) {
            event.target.select();
        },

        handleKeydown(e) {
            const target = e.target;
            if (target.tagName !== 'INPUT') return;

            const day = parseInt(target.dataset.day);
            const cIdx = parseInt(target.dataset.cidx);
            let nextDay = day;
            let nextCIdx = cIdx;

            switch(e.key) {
                case 'ArrowUp':
                    e.preventDefault();
                    if (day > 1) nextDay = day - 1;
                    break;
                case 'ArrowDown':
                    e.preventDefault();
                    if (day < this.daysInMonth.length) nextDay = day + 1;
                    break;
                case 'ArrowLeft':
                    if (target.selectionStart === 0) {
                        e.preventDefault();
                        if (cIdx > 0) nextCIdx = cIdx - 1;
                    }
                    break;
                case 'ArrowRight':
                    if (target.selectionEnd === target.value.length) {
                        e.preventDefault();
                        if (cIdx < this.controls.length - 1) nextCIdx = cIdx + 1;
                    }
                    break;
                case 'Enter':
                    e.preventDefault();
                    if (day < this.daysInMonth.length) nextDay = day + 1;
                    else if (cIdx < this.controls.length - 1) {
                        nextDay = 1;
                        nextCIdx = cIdx + 1;
                    }
                    break;
            }

            if (nextDay !== day || nextCIdx !== cIdx) {
                this.$nextTick(() => {
                    const nextInput = document.querySelector(`input[data-day="${nextDay}"][data-cidx="${nextCIdx}"]`);
                    if (nextInput) nextInput.focus();
                });
            }
        },

        setupKeyboard() {
            document.addEventListener('keydown', (e) => {
                if ((e.ctrlKey || e.metaKey) && e.key === 's') {
                    e.preventDefault();
                    if (this.canSave) {
                        this.saveAll();
                    }
                }
            });
        }
    }));
});
</script>
<?= $this->endSection(); ?>