const COLORS = ['#0099ff', '#ff9900', '#00cc00', '#996633', '#94a3b8', '#ffffff', '#cc0000', '#000000', '#ffff00', '#cc00cc', '#ff99cc', '#00cccc'];
const COLOR_NAMES = ['Blue', 'Orange', 'Green', 'Brown', 'Slate', 'White', 'Red', 'Black', 'Yellow', 'Violet', 'Rose', 'Aqua'];

class FiberApp {
    constructor() {
        this.cables = []; this.splices = []; this.projectId = null; 
        this.scale = 1.0; this.dragCableId = null; this.contextCableId = null; 
        this.selectedNode = null; this.isDragging = false; this.dragOffset = { x: 0, y: 0 };
        this.autosaveTimeout = null; 
        
        this.viewport = document.getElementById('viewport');
        this.mainContainer = document.getElementById('fiber-main');
        this.cablesLayer = document.getElementById('cablesLayer');
        this.svgLayer = document.getElementById('connectionsLayer');
        this.contextMenu = document.getElementById('contextMenu');
        
        this.viewport.style.transform = `scale(${this.scale})`;
        this.bindEvents();
        
        if (typeof INITIAL_PROJECT_ID !== 'undefined' && INITIAL_PROJECT_ID) {
            this.loadProjectsList().then(() => {
                this.loadProjectData(INITIAL_PROJECT_ID);
            });
        } else {
            this.loadProjectsList();
            if(this.cables.length === 0) {
                this.addCable('source', 100, 100, 12, 'FEEDER');
            }
        }
    }

    async api(action, data = {}) {
        data.action = action;
        const res = await fetch('fiber_api.php', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(data)
        });
        return await res.json();
    }

    async loadProjectsList() {
        try {
            const res = await this.api('list');
            if(res.status === 'success') {
                const select = document.getElementById('projectSelect');
                select.innerHTML = '<option value="">-- Load Project --</option>' + 
                    res.projects.map(p => `<option value="${p.id}">${p.project_name}</option>`).join('');
            }
        } catch(e) { console.error("API Error", e); }
    }

    async loadProjectData(id) {
        if(!id) return;
        const res = await this.api('load', { id });
        if(res.status === 'success') {
            const doc = res.project;
            this.projectId = doc.id;
            
            // Safety catch for migrated stringified JSON data
            let cData = doc.cable_data;
            if (typeof cData === 'string') {
                try { cData = JSON.parse(cData); } catch(e) { cData = { cables: [], splices: [] }; }
            }
            
            // Sanitize legacy spliceboxes into standard destination blocks
            this.cables = (cData.cables || []).map(c => {
                if (c.type === 'splicebox') c.type = 'dest';
                return c;
            });
            this.splices = cData.splices || [];
            
            this.cablesLayer.innerHTML = '';
            this.cables.forEach(c => this.renderCable(c));

            // Wait for fonts to load and layout to settle, then render connections once
            document.fonts.ready.then(() => {
                requestAnimationFrame(() => requestAnimationFrame(() => {
                    this.renderConnections();
                }));
            });
            
            document.getElementById('projectSelect').value = id;
            window.history.replaceState({}, '', `cable_map.php?id=${id}`);
        }
    }

    triggerAutosave() {
        if (!this.projectId) return; 
        
        const btn = document.getElementById('btnSave');
        btn.innerHTML = '<i class="fas fa-sync fa-spin mr-2"></i> SAVING...';
        btn.classList.remove('btn-green', 'btn-red', 'shadow-lg', 'shadow-emerald-900/40');
        btn.classList.add('btn-slate'); 
        
        clearTimeout(this.autosaveTimeout);
        this.autosaveTimeout = setTimeout(() => {
            this.saveProject(true);
        }, 1000);
    }

    async saveProject(isAutosave = false) {
        if (!this.projectId) {
            if (!isAutosave) alert("Save error: Project ID missing. Start from the Projects list."); 
            return;
        }
        
        const s = document.getElementById('projectSelect'); 
        let name = s.options[s.selectedIndex] ? s.options[s.selectedIndex].text : "Untitled Project"; 

        const btn = document.getElementById('btnSave');
        if (!isAutosave) {
            btn.innerHTML = '<i class="fas fa-sync fa-spin mr-2"></i> SAVING...';
            btn.classList.remove('btn-green', 'btn-red', 'shadow-lg', 'shadow-emerald-900/40');
            btn.classList.add('btn-slate');
        }

        const res = await this.api('save', {
            id: this.projectId, name: name,
            data: { cables: this.cables, splices: this.splices }
        });

        if(res.status === 'success') {
            btn.innerHTML = "<i class='fas fa-check mr-1'></i> SAVED";
            btn.classList.remove('btn-slate', 'btn-red');
            btn.classList.add('btn-green', 'shadow-lg', 'shadow-emerald-900/40'); 
        } else {
            if (!isAutosave) alert("Save failed: " + res.message);
            btn.innerHTML = "<i class='fas fa-exclamation-triangle mr-2'></i> ERROR";
            btn.classList.remove('btn-slate', 'btn-green', 'shadow-lg', 'shadow-emerald-900/40');
            btn.classList.add('btn-red');
        }
    }

    async renameProject() {
        if (!this.projectId) return alert("Select a project first.");
        const select = document.getElementById('projectSelect');
        const currentName = select.options[select.selectedIndex].text;
        const newName = prompt("Rename Project:", currentName);
        
        if (newName && newName.trim() !== "") { 
            const res = await this.api('rename', { id: this.projectId, name: newName.trim() });
            if(res.status === 'success') {
                this.loadProjectsList().then(() => {
                    document.getElementById('projectSelect').value = this.projectId;
                });
            }
        }
    }

    addCable(type = 'dest', x = 400, y = 100, count = 12, name = null) {
        const id = `cable_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
        const defaultName = `Cable ${String.fromCharCode(65 + this.cables.length)}`;
        
        const newCable = { id, type, x, y, count, name: name || defaultName, activeTube: 1, notes: {}, distances: {} };
        
        this.cables.push(newCable);
        this.renderCable(newCable);
        
        if (this.projectId) this.triggerAutosave();
        return newCable;
    }

    addSplice(fromNode, toNode) {
        if (fromNode === toNode) return;
        const exists = this.splices.find(s => (s.from === fromNode && s.to === toNode) || (s.from === toNode && s.to === fromNode));
        if (exists) return;

        const isNodeFull = (nodeId) => {
            const connections = this.splices.filter(s => s.from === nodeId || s.to === nodeId).length;
            return connections >= 1; // Strict 1-to-1 connection
        };

        if (isNodeFull(fromNode) || isNodeFull(toNode)) return alert("Fiber busy!");
        this.splices.push({ from: fromNode, to: toNode }); 
        this.renderConnections();
        this.triggerAutosave();
    }

    actionAutoMap() {
        const sortedCables = [...this.cables].sort((a, b) => a.x - b.x);
        if (sortedCables.length < 2) return alert("Need at least 2 objects positioned left-to-right.");
        let totalSplices = 0;
        for (let i = 0; i < sortedCables.length - 1; i++) {
            const source = sortedCables[i]; const target = sortedCables[i+1];
            const limit = Math.min(source.count, target.count);
            for (let f = 1; f <= limit; f++) {
                const fromNode = `${source.id}_${f}`; const toNode = `${target.id}_${f}`;
                const exists = this.splices.find(s => (s.from === fromNode && s.to === toNode) || (s.from === toNode && s.to === fromNode));
                if (exists) continue;
                const getConnCount = (id) => this.splices.filter(s => s.from === id || s.to === id).length;
                if (getConnCount(fromNode) < 1 && getConnCount(toNode) < 1) {
                    this.splices.push({ from: fromNode, to: toNode }); totalSplices++;
                }
            }
        }
        this.renderConnections();
        if(totalSplices > 0) {
            alert(`Chain mapped! Created ${totalSplices} connections.`);
            this.triggerAutosave();
        } else {
            alert("No new connections possible.");
        }
    }

    renderCable(cable) {
        const existing = document.getElementById(cable.id);
        if (existing) existing.remove();
        
        const el = document.createElement('div');
        let typeClass = 'type-dest'; let headerColor = 'text-emerald-600 dark:text-emerald-400';
        if (cable.type === 'source') { typeClass = 'type-source'; headerColor = 'text-blue-600 dark:text-blue-400'; }

        el.className = `cable-card ${typeClass}`;
        el.id = cable.id;
        el.style.transform = `translate(${cable.x}px, ${cable.y}px)`;
        
        const totalTubes = Math.ceil(cable.count / 12);
        let tubeSelectorHTML = '';
        if (totalTubes > 1) {
            let options = '';
            for(let t = 1; t <= totalTubes; t++) {
                const tubeColor = COLOR_NAMES[(t-1)%12];
                const start = (t-1)*12 + 1; const end = Math.min(t*12, cable.count);
                const selected = cable.activeTube == t ? 'selected' : '';
                options += `<option value="${t}" ${selected}>Tube ${t} (${tubeColor}): ${start}-${end}</option>`;
            }
            tubeSelectorHTML = `<select class="tube-select" onchange="app.changeTube('${cable.id}', this.value)" onmousedown="event.stopPropagation()">${options}</select>`;
        }

        let typeLabel = (cable.type || 'unknown').toUpperCase();

        el.innerHTML = `
            <div class="cable-header p-3 border-b border-gray-200 dark:border-white/5 bg-gray-100/80 dark:bg-slate-900/50 rounded-t-lg">
                <div class="flex justify-between items-start">
                    <div class="w-full">
                        <div class="text-[9px] text-gray-500 dark:text-slate-500 font-bold uppercase tracking-wider flex justify-between items-center pr-2">
                            ${typeLabel}
                            <span class="editable text-gray-500 dark:text-slate-600 hover:text-gray-900 dark:hover:text-white" title="Click to change size" onclick="app.editSize(this, '${cable.id}')">${cable.count}F</span>
                        </div>
                        <div class="editable cable-name-text text-xs font-bold ${headerColor} truncate w-48" title="Click to rename" onclick="app.editName(this, '${cable.id}')">${cable.name}</div>
                        ${tubeSelectorHTML}
                    </div>
                    <button class="text-gray-400 dark:text-slate-500 hover:text-gray-900 dark:hover:text-white px-2 font-bold text-lg leading-none" onmousedown="app.openMenu(event, '${cable.id}')">⋮</button>
                </div>
            </div>
            <div class="node-list bg-gray-50/50 dark:bg-slate-900/20"></div>`;

        const list = el.querySelector('.node-list');
        const startNode = totalTubes > 1 ? (cable.activeTube - 1) * 12 + 1 : 1;
        const endNode = totalTubes > 1 ? Math.min(cable.activeTube * 12, cable.count) : cable.count;

        for (let i = startNode; i <= endNode; i++) {
            const color = COLORS[(i - 1) % 12];
            const nodeId = `${cable.id}_${i}`;
            const row = document.createElement('div');
            row.className = 'node-row cursor-pointer'; row.dataset.nodeId = nodeId;
            row.onclick = (e) => { e.stopPropagation(); this.handleNodeClick(cable.id, i, nodeId); };
            
            const savedNote = (cable.notes && cable.notes[i]) ? cable.notes[i] : '';
            const noteHTML = `<input type="text" class="small-input note-input" value="${savedNote}" placeholder="..." onmousedown="event.stopPropagation()" onclick="event.stopPropagation()" oninput="app.updateNote('${cable.id}', ${i}, this.value)">`;
            
            let distHTML = '';
            if (cable.type === 'source') {
                const savedDist = (cable.distances && cable.distances[i]) ? cable.distances[i] : '';
                distHTML = `<input type="text" class="small-input dist-input" value="${savedDist}" placeholder="Dist" onmousedown="event.stopPropagation()" onclick="event.stopPropagation()" oninput="app.updateDistance('${cable.id}', ${i}, this.value)">`;
            }

            row.innerHTML = `<div class="flex items-center gap-2" style="min-width: 45px;"><div class="node-dot" style="background-color: ${color}; box-shadow: 0 0 5px ${color}44;"></div><span class="font-bold w-5 text-right">${i}</span></div><div class="flex items-center">${distHTML}${noteHTML}</div>`;
            list.appendChild(row);
        }
        
        this.cablesLayer.appendChild(el);
    }

    updateNote(cableId, fiberIdx, text) { 
        const c = this.cables.find(x => x.id === cableId); 
        if(c) { 
            if(!c.notes) c.notes = {}; 
            c.notes[fiberIdx] = text; 
            this.triggerAutosave();
        } 
    }
    updateDistance(cableId, fiberIdx, text) { 
        const c = this.cables.find(x => x.id === cableId); 
        if(c) { 
            if(!c.distances) c.distances = {}; 
            c.distances[fiberIdx] = text; 
            this.triggerAutosave();
        } 
    }

    printManifest() {
        const s = document.getElementById('projectSelect'); 
        const pName = this.projectId ? s.options[s.selectedIndex].text : "Untitled Project";
        document.getElementById('printTitle').innerText = pName;
        const tbody = document.getElementById('printBody'); 
        tbody.innerHTML = '';

        let graph = {};
        this.splices.forEach(sp => {
            if(!graph[sp.from]) graph[sp.from] = [];
            if(!graph[sp.to]) graph[sp.to] = [];
            graph[sp.from].push(sp.to);
            graph[sp.to].push(sp.from);
        });

        let visited = new Set();
        let reportData = [];

        const parseNode = (nodeId) => {
            let parts = nodeId.split('_');
            let strand = parseInt(parts.pop());
            let cableId = parts.join('_');
            let cable = this.cables.find(c => c.id === cableId);
            return { cable, strand, nodeId };
        };

        const processPath = (pathNodes) => {
            let srcNum = '-', destNum = '-';
            let dist = '';
            let notes = [];

            pathNodes.forEach(nodeId => {
                let { cable, strand } = parseNode(nodeId);
                if(!cable) return;

                if (cable.type === 'source') {
                    srcNum = strand;
                    if(cable.distances && cable.distances[strand]) dist = cable.distances[strand];
                    if(cable.notes && cable.notes[strand]) notes.push(`[${cable.name}] ${cable.notes[strand]}`);
                } else if (cable.type === 'dest') {
                    destNum = strand;
                    if(cable.notes && cable.notes[strand]) notes.push(`[${cable.name}] ${cable.notes[strand]}`);
                }
            });

            reportData.push({
                sortKey: srcNum !== '-' ? parseInt(srcNum) : 9999, 
                srcNum, destNum, dist, 
                note: notes.join(' | ')
            });
        };

        this.cables.filter(c => c.type === 'source').forEach(src => {
            for(let i=1; i<=src.count; i++) {
                let startNode = `${src.id}_${i}`;
                if (graph[startNode] && graph[startNode].length > 0 && !visited.has(startNode)) {
                    let path = [];
                    let curr = startNode;
                    while (curr) {
                        path.push(curr);
                        visited.add(curr);
                        let neighbors = graph[curr] || [];
                        let next = neighbors.find(n => !visited.has(n));
                        curr = next;
                    }
                    processPath(path);
                }
            }
        });

        Object.keys(graph).forEach(node => {
            if (!visited.has(node)) {
                let path = [];
                let curr = node;
                while (curr) {
                    path.push(curr);
                    visited.add(curr);
                    let neighbors = graph[curr] || [];
                    let next = neighbors.find(n => !visited.has(n));
                    curr = next;
                }
                processPath(path);
            }
        });

        reportData.sort((a, b) => a.sortKey - b.sortKey);

        if(reportData.length === 0) {
            tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding:20px;">No active connections found.</td></tr>';
        } else {
            reportData.forEach((row) => {
                const tr = document.createElement('tr');
                tr.innerHTML = `
                    <td class="font-bold text-center text-blue-800">${row.srcNum}</td>
                    <td class="text-center font-bold text-emerald-800">${row.destNum}</td>
                    <td class="text-center">${row.dist}</td>
                    <td style="color:#444;">${row.note}</td>`;
                tbody.appendChild(tr);
            });
        }
        window.print();
    }

    renderConnections() {
        this.svgLayer.innerHTML = ''; 
        const rect = this.viewport.getBoundingClientRect();
        
        this.splices.forEach(splice => {
            const elFrom = document.querySelector(`[data-node-id="${splice.from}"]`); 
            const elTo = document.querySelector(`[data-node-id="${splice.to}"]`);
            if (!elFrom || !elTo) return;
            
            const r1 = elFrom.getBoundingClientRect(); 
            const r2 = elTo.getBoundingClientRect();
            
            const isReversed = r1.left > r2.left;

            // Find dead-center of row height dynamically
            const x1 = isReversed ? (r1.left - rect.left) / this.scale : (r1.right - rect.left) / this.scale; 
            const y1 = (r1.top + r1.height/2 - rect.top) / this.scale;
            
            const x2 = isReversed ? (r2.right - rect.left) / this.scale : (r2.left - rect.left) / this.scale; 
            const y2 = (r2.top + r2.height/2 - rect.top) / this.scale;
            
            const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
            const dist = Math.abs(x2 - x1); 
            const cpOffset = Math.max(dist * 0.4, 40);
            
            if (isReversed) {
                path.setAttribute("d", `M ${x1} ${y1} C ${x1 - cpOffset} ${y1}, ${x2 + cpOffset} ${y2}, ${x2} ${y2}`);
            } else {
                path.setAttribute("d", `M ${x1} ${y1} C ${x1 + cpOffset} ${y1}, ${x2 - cpOffset} ${y2}, ${x2} ${y2}`);
            }
            
            path.setAttribute("stroke", "#ea580c"); 
            path.setAttribute("stroke-width", "2"); 
            path.setAttribute("opacity", "0.6");
            
            path.onclick = (e) => { 
                e.stopPropagation(); 
                if(confirm("Disconnect splice?")) { 
                    this.splices = this.splices.filter(s => s !== splice); 
                    this.renderConnections(); 
                    this.triggerAutosave();
                }
            };
            this.svgLayer.appendChild(path);
        });
    }

    editName(el, id) {
        const current = el.innerText;
        const input = document.createElement('input'); input.value = current; input.className = 'edit-input';
        const save = () => { 
            const val = input.value.trim(); 
            if(val) { 
                const c = this.cables.find(x => x.id === id); 
                if(c) { 
                    c.name = val; 
                    this.renderCable(c); 
                    this.triggerAutosave();
                } 
            } else { 
                el.innerText = current; input.replaceWith(el); 
            } 
        };
        input.onblur = save; input.onkeydown = (e) => { if(e.key === 'Enter') save(); }; el.replaceWith(input); input.focus();
    }
    
    editSize(el, id) {
        const current = parseInt(el.innerText);
        const input = document.createElement('input'); input.type = 'number'; input.value = current; input.className = 'edit-input'; input.style.width = '40px';
        const save = () => { 
            const val = parseInt(input.value); 
            if(val && val > 0 && val <= 864) { 
                const c = this.cables.find(x => x.id === id); 
                if(c) { 
                    c.count = val; 
                    this.renderCable(c); 
                    // Auto-sync after size change
                    setTimeout(() => this.renderConnections(), 50);
                    setTimeout(() => this.renderConnections(), 200);
                    this.triggerAutosave();
                } 
            } else { 
                el.innerText = current + 'F'; input.replaceWith(el); 
            } 
        };
        input.onblur = save; input.onkeydown = (e) => { if(e.key === 'Enter') save(); }; el.replaceWith(input); input.focus();
    }
    
    changeTube(cableId, tubeIndex) { 
        const cable = this.cables.find(c => c.id === cableId); 
        if(cable) { 
            cable.activeTube = parseInt(tubeIndex); 
            this.renderCable(cable); 
            // Auto-sync after tube change
            setTimeout(() => this.renderConnections(), 50);
            setTimeout(() => this.renderConnections(), 200);
            this.triggerAutosave();
        } 
    }
    
    bindEvents() {
        this.viewport.addEventListener('wheel', (e) => { e.preventDefault(); this.zoom(e.deltaY > 0 ? -0.1 : 0.1); }, { passive: false });
        
        this.viewport.addEventListener('mousedown', (e) => {
            if(e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT' || e.target.closest('#contextMenu') || e.target.classList.contains('editable')) return;
            const header = e.target.closest('.cable-header');
            if (header) {
                const card = header.closest('.cable-card'); this.isDragging = true; this.dragCableId = card.id;
                const rect = card.getBoundingClientRect(); 
                this.dragOffset.x = (e.clientX - rect.left) / this.scale; 
                this.dragOffset.y = (e.clientY - rect.top) / this.scale;
                this.cablesLayer.appendChild(card); this.contextMenu.classList.add('hidden'); 
            } else { this.contextMenu.classList.add('hidden'); }
        });
        
        window.addEventListener('mousemove', (e) => {
            if (this.isDragging && this.dragCableId) {
                const card = document.getElementById(this.dragCableId); const cable = this.cables.find(c => c.id === this.dragCableId);
                const rect = this.viewport.getBoundingClientRect();
                
                let x = (e.clientX - rect.left) / this.scale - this.dragOffset.x; 
                let y = (e.clientY - rect.top) / this.scale - this.dragOffset.y;
                
                x = Math.round(x / 20) * 20; y = Math.round(y / 20) * 20; 
                cable.x = x; cable.y = y;
                card.style.transform = `translate(${x}px, ${y}px)`; this.renderConnections();
            }
        });
        
        window.addEventListener('mouseup', () => { 
            if (this.isDragging && this.dragCableId) {
                this.triggerAutosave(); 
            }
            this.isDragging = false; 
            this.dragCableId = null; 
        });
        
        document.getElementById('btnAddSource').onclick = () => this.addCable('source', 100, 100, 12, 'FEEDER');
        document.getElementById('btnAddDest').onclick = () => this.addCable('dest', 400, 100, 12, null);
        
        document.getElementById('btnSave').onclick = () => this.saveProject();
        document.getElementById('projectSelect').onchange = (e) => this.loadProjectData(e.target.value);
        document.getElementById('btnRename').onclick = () => this.renameProject();
        document.getElementById('btnAutoMap').onclick = () => this.actionAutoMap();
        document.getElementById('btnClearSplices').onclick = () => this.actionClearSplices();
    }
    
    zoom(delta) { this.scale = Math.max(0.4, Math.min(3.0, this.scale + delta)); this.viewport.style.transform = `scale(${this.scale})`; document.getElementById('zoomLabel').innerText = `${Math.round(this.scale * 100)}%`; }
    
    handleNodeClick(cableId, index, nodeId) {
        if (this.selectedNode && this.selectedNode.id === nodeId) return this.clearSelection();
        if (!this.selectedNode) { this.selectedNode = { id: nodeId, cableId }; document.querySelector(`[data-node-id="${nodeId}"]`).classList.add('active-selection'); return; }
        if (this.selectedNode.cableId === cableId) return; 
        this.addSplice(this.selectedNode.id, nodeId); this.clearSelection();
    }
    
    clearSelection() { if(this.selectedNode) { const el = document.querySelector(`[data-node-id="${this.selectedNode.id}"]`); if(el) el.classList.remove('active-selection'); this.selectedNode = null; } }
    
    actionClearSplices() { 
        if(confirm("Unpatch ALL?")) { 
            this.splices = []; 
            this.renderConnections(); 
            this.cables.forEach(c => this.renderCable(c)); 
            this.triggerAutosave();
        } 
    }
    
    openMenu(e, cableId) { 
        e.preventDefault(); e.stopPropagation(); 
        this.contextCableId = cableId; 
        const rect = this.viewport.getBoundingClientRect();
        this.contextMenu.style.left = `${e.clientX - rect.left}px`; 
        this.contextMenu.style.top = `${e.clientY - rect.top}px`; 
        this.contextMenu.classList.remove('hidden'); 
    }
    
    actionDelete() { 
        if(!confirm("Delete?")) return; 
        const id = this.contextCableId; 
        this.splices = this.splices.filter(s => !s.from.startsWith(id) && !s.to.startsWith(id)); 
        this.cables = this.cables.filter(c => c.id !== id); 
        const existing = document.getElementById(id);
        if (existing) {
            existing.remove();
        }
        this.renderConnections(); 
        this.contextMenu.classList.add('hidden'); 
        this.triggerAutosave();
    }
}
const app = new FiberApp();