@extends('layouts.app')

@section('title', '조직도 관리')

@section('content')
<div x-data="orgChart()" x-init="init()" @click="handleClick($event)" @dblclick="handleDblClick($event)">
    <div class="flex justify-between items-center mb-6">
        <h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2">
            <i class="ri-organization-chart text-purple-600"></i>
            조직도 관리
        </h1>
        <div class="flex items-center gap-3">
            <span class="text-sm text-gray-500" x-text="`전체 ${totalEmployees}명 | 배치 ${assignedCount}명 | 미배치 ${unassignedCount}명`"></span>
            <a href="{{ route('rd.index') }}" class="bg-white hover:bg-gray-100 text-gray-700 px-4 py-2 rounded-lg border transition text-sm">
                <i class="ri-arrow-left-line"></i> 돌아가기
            </a>
        </div>
    </div>

    <div class="bg-blue-50 border border-blue-200 rounded-lg px-4 py-3 mb-6 text-sm text-blue-700 flex items-start gap-2">
        <i class="ri-information-line text-lg shrink-0 mt-0.5"></i>
        <span>직원 카드를 드래그하여 부서에 배치합니다. 부서 헤더를 드래그하여 순서를 변경하거나 다른 부서 아래로 이동할 수 있습니다. 부서 헤더를 <b>더블클릭</b>하면 숨기기 버튼이 나타납니다.</span>
    </div>

    <!-- 미배치 직원 패널 -->
    <div class="mb-8">
        <div class="bg-white rounded-lg shadow-sm border border-gray-200">
            <div class="flex items-center gap-2 px-4 py-3 bg-orange-50 border-b border-orange-200 rounded-t-lg cursor-pointer select-none"
                 @click="unassignedOpen = !unassignedOpen">
                <i class="ri-arrow-down-s-line transition-transform text-gray-400" :class="!unassignedOpen && '-rotate-90'"></i>
                <i class="ri-user-unfollow-line text-orange-500"></i>
                <span class="font-semibold text-gray-700 text-sm">미배치 직원</span>
                <span class="bg-orange-100 text-orange-600 text-xs font-bold px-2 py-0.5 rounded-full" x-text="unassignedCount"></span>
                <div class="ml-auto flex items-center gap-2" @click.stop>
                    <input type="text" x-model="searchUnassigned" @input="renderUnassigned()" placeholder="이름 검색..."
                           class="px-3 py-1 border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-purple-400"
                           style="width: 160px;">
                </div>
            </div>
            <div x-show="unassignedOpen" x-collapse>
                <div x-ref="unassignedZone" id="unassigned-zone" class="p-3 flex flex-wrap gap-2" data-department-id="" style="min-height: 50px;"></div>
            </div>
        </div>
    </div>

    <!-- 숨겨진 부서 패널 -->
    <div x-show="hiddenDeptList.length > 0" class="mb-4">
        <div class="bg-white rounded-lg shadow-sm border border-gray-200 px-4 py-3">
            <div class="flex items-center gap-2 mb-2">
                <i class="ri-eye-off-line text-gray-400"></i>
                <span class="text-sm font-semibold text-gray-600">숨겨진 부서</span>
                <span class="bg-gray-100 text-gray-500 text-xs font-bold px-2 py-0.5 rounded-full" x-text="hiddenDeptList.length"></span>
            </div>
            <div class="flex flex-wrap gap-2">
                <template x-for="dept in hiddenDeptList" :key="dept.id">
                    <span class="inline-flex items-center gap-1.5 bg-gray-50 border border-gray-200 rounded-lg px-3 py-1.5 text-sm text-gray-700">
                        <span x-text="dept.name"></span>
                        <button data-action="restore-dept" :data-dept-id="dept.id"
                                class="text-purple-500 hover:text-purple-700 transition" title="다시 표시">
                            <i class="ri-eye-line text-sm"></i>
                        </button>
                    </span>
                </template>
            </div>
        </div>
    </div>

    <!-- 조직도 트리 -->
    <div class="overflow-x-auto pb-8">
        <div class="org-tree" style="display:flex;flex-direction:column;align-items:center;">
            <div class="rounded-xl px-6 py-4 shadow-lg text-center"
                 style="background:linear-gradient(135deg,#7C3AED,#4338CA);color:#fff;">
                <div class="flex items-center justify-center gap-2 mb-1">
                    <i class="ri-building-4-line text-lg"></i>
                    <span class="font-bold text-lg" x-text="companyName"></span>
                </div>
                <p class="text-sm" style="color:#E0D4FC;" x-text="ceoName ? `대표이사 ${ceoName}` : ''"></p>
            </div>
            <div x-show="rootDepts.length > 0" style="width:1px;height:24px;background:#D1D5DB;"></div>
            <div x-ref="deptTree"></div>
        </div>
    </div>

    <div x-show="saving" x-transition
         class="fixed bottom-6 right-6 bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg text-sm flex items-center gap-2 z-50">
        <i class="ri-loader-4-line animate-spin"></i> 저장 중...
    </div>
</div>

<style>
.org-children { display:flex; justify-content:center; position:relative; }
.org-node-wrap { display:flex; flex-direction:column; align-items:center; position:relative; padding:0 12px; }
.org-children > .org-node-wrap:not(:first-child)::before {
    content:''; position:absolute; top:0; left:0; right:50%; height:1px; background:#D1D5DB;
}
.org-children > .org-node-wrap:not(:last-child)::after {
    content:''; position:absolute; top:0; left:50%; right:0; height:1px; background:#D1D5DB;
}
.org-children > .org-node-wrap:only-child::before,
.org-children > .org-node-wrap:only-child::after { display:none; }
.employee-card.sortable-ghost, .org-node-wrap.sortable-ghost { opacity:0.3; }
.employee-card.sortable-drag { box-shadow:0 8px 25px rgba(0,0,0,0.15); z-index:999; }
.org-node-wrap.sortable-drag { box-shadow:0 8px 25px rgba(0,0,0,0.2); z-index:999; }
body.dept-dragging .org-drop-target {
    min-height:28px; border:2px dashed #C4B5FD; border-radius:8px;
    font-size:10px; color:#A78BFA; padding:4px; margin-top:4px;
}
.org-drop-target {
    min-height:0; border:none; padding:0; margin:0; overflow:hidden;
    display:flex; align-items:center; justify-content:center;
    font-size:0; color:transparent; transition:all 0.2s;
}
</style>
@endsection

@push('scripts')
<script>
function orgChart() {
    return {
        departments: @json($departments),
        employees: @json($employees),
        companyName: @json($companyName),
        ceoName: @json($ceoName),
        searchUnassigned: '',
        unassignedOpen: true,
        saving: false,
        deptSortables: [],
        empSortables: [],
        unassignedSortable: null,
        hiddenDepts: new Set(@json($departments->filter(fn($d) => ($d->options['orgchart_hidden'] ?? false))->pluck('id')->values())),
        dblClickDept: null,
        execTitles: ['대표이사', '사장', '부사장', '회장', '부회장'],

        isExecutive(emp) {
            const pos = (emp.position_label || '').trim();
            if (this.execTitles.includes(pos)) return true;
            if (this.ceoName && emp.display_name === this.ceoName) return true;
            return false;
        },
        get normalEmployees() { return this.employees.filter(e => !this.isExecutive(e)); },
        get totalEmployees() { return this.normalEmployees.length; },
        get assignedCount() { return this.normalEmployees.filter(e => e.department_id).length; },
        get unassignedCount() { return this.normalEmployees.filter(e => !e.department_id).length; },
        get rootDepts() { return this.getChildrenSorted(null).filter(d => !this.isDeptHidden(d.id)); },

        getChildrenSorted(parentId) {
            return this.departments
                .filter(d => (d.parent_id || null) === parentId)
                .sort((a, b) => (a.sort_order ?? 999) - (b.sort_order ?? 999) || (a.name || '').localeCompare(b.name || ''));
        },
        getDeptEmployees(deptId) { return this.employees.filter(e => e.department_id === deptId); },
        getDeptTotalCount(deptId) {
            let c = this.getDeptEmployees(deptId).length;
            for (const ch of this.departments.filter(d => d.parent_id === deptId)) c += this.getDeptTotalCount(ch.id);
            return c;
        },
        isDescendant(ancestorId, targetId) {
            if (!targetId) return false;
            if (targetId === ancestorId) return true;
            const p = this.departments.find(d => d.id === targetId);
            return p ? this.isDescendant(ancestorId, p.parent_id) : false;
        },
        getAvatarColor(name) {
            if (!name) return '#9CA3AF';
            const c = ['#6366F1','#8B5CF6','#EC4899','#F59E0B','#10B981','#3B82F6','#EF4444','#14B8A6','#F97316','#6D28D9'];
            let h = 0; for (let i = 0; i < name.length; i++) h = name.charCodeAt(i) + ((h << 5) - h);
            return c[Math.abs(h) % c.length];
        },
        esc(s) { return s ? String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;') : ''; },

        init() {
            this.renderTree();
            this.renderUnassigned();
            this.initUnassignedSortable();
        },

        // ===== 미배치 직원 =====
        renderUnassigned() {
            const zone = this.$refs.unassignedZone;
            if (!zone) return;
            let emps = this.employees.filter(e => !e.department_id && !this.isExecutive(e));
            if (this.searchUnassigned) {
                const q = this.searchUnassigned.toLowerCase();
                emps = emps.filter(e => (e.display_name || '').toLowerCase().includes(q));
            }
            if (!emps.length) {
                zone.innerHTML = '<div style="width:100%;text-align:center;color:#9CA3AF;font-size:12px;padding:16px 0;"><i class="ri-user-smile-line" style="font-size:24px;display:block;margin-bottom:4px;"></i>미배치 직원 없음</div>';
                return;
            }
            zone.innerHTML = emps.map(e => this.buildEmpHtml(e, true)).join('');
        },

        initUnassignedSortable() {
            const zone = document.getElementById('unassigned-zone');
            if (!zone) return;
            this.unassignedSortable = new Sortable(zone, {
                group: 'employees', animation: 150,
                ghostClass: 'sortable-ghost', dragClass: 'sortable-drag',
                onAdd: (evt) => {
                    const empId = parseInt(evt.item.dataset.employeeId);
                    evt.item.remove();
                    const emp = this.employees.find(e => e.id === empId);
                    if (emp) emp.department_id = null;
                    this.renderTree();
                    this.renderUnassigned();
                    this.saveAssignment(empId, null);
                },
            });
        },

        // ===== 부서 트리 =====
        renderTree() {
            this.deptSortables.forEach(s => s.destroy()); this.deptSortables = [];
            this.empSortables.forEach(s => s.destroy()); this.empSortables = [];
            const el = this.$refs.deptTree;
            if (!el) return;
            el.innerHTML = this.buildChildrenHtml(null, 0);
            this.$nextTick(() => { this.initDeptSortables(); this.initEmpSortables(); });
        },

        buildChildrenHtml(parentId, level) {
            const children = this.getChildrenSorted(parentId).filter(d => !this.isDeptHidden(d.id));
            if (!children.length && parentId !== null) {
                return `<div class="org-children org-drop-target" data-parent-id="${parentId}">하위 부서 드롭</div>`;
            }
            if (!children.length) return '';
            let h = `<div class="org-children" data-parent-id="${parentId ?? ''}">`;
            for (const d of children) h += this.buildNodeHtml(d, level);
            h += '</div>';
            return h;
        },

        buildNodeHtml(dept, level) {
            const emps = this.getDeptEmployees(dept.id);
            const children = this.getChildrenSorted(dept.id);
            const visibleChildren = children.filter(d => !this.isDeptHidden(d.id));
            const total = this.getDeptTotalCount(dept.id);
            const styles = [
                { border:'2px solid #C4B5FD', hBg:'#F5F3FF', hBdr:'#E9D5FF', icon:'ri-building-2-line', iconClr:'#7C3AED', w:'200px' },
                { border:'1px solid #C7D2FE', hBg:'#EEF2FF', hBdr:'#C7D2FE', icon:'ri-git-branch-line', iconClr:'#6366F1', w:'180px' },
                { border:'1px solid #E5E7EB', hBg:'#F9FAFB', hBdr:'#E5E7EB', icon:'ri-subtract-line', iconClr:'#6B7280', w:'160px' },
            ];
            const s = styles[Math.min(level, styles.length - 1)];

            let h = `<div class="org-node-wrap" data-dept-id="${dept.id}">`;
            h += `<div style="width:1px;height:24px;background:#D1D5DB;"></div>`;
            h += `<div style="width:${s.w};border:${s.border};border-radius:12px;background:#fff;text-align:center;overflow:hidden;">`;
            h += `<div class="dept-drag-handle" data-action="dept-dblclick" data-dept-id="${dept.id}" style="cursor:grab;padding:8px 12px;background:${s.hBg};border-bottom:1px solid ${s.hBdr};display:flex;align-items:center;justify-content:center;gap:4px;position:relative;">`;
            h += `<i class="ri-drag-move-2-line" style="color:${s.iconClr};opacity:0.5;font-size:11px;"></i>`;
            h += `<i class="${s.icon}" style="color:${s.iconClr};font-size:13px;"></i>`;
            h += `<span style="font-weight:700;font-size:13px;color:#1F2937;">${this.esc(dept.name)}</span>`;
            if (dept.code) h += `<span style="font-size:11px;color:#9CA3AF;">(${this.esc(dept.code)})</span>`;
            if (this.dblClickDept === dept.id) {
                h += `<button data-action="hide-dept" data-dept-id="${dept.id}" style="position:absolute;right:4px;top:50%;transform:translateY(-50%);background:#EF4444;color:#fff;border:none;border-radius:6px;padding:2px 8px;font-size:11px;cursor:pointer;white-space:nowrap;z-index:10;">숨기기</button>`;
            }
            h += '</div>';
            h += `<div class="emp-zone" data-department-id="${dept.id}" style="padding:6px;min-height:36px;">`;
            if (emps.length) {
                for (const e of emps) h += this.buildEmpHtml(e, false);
            } else {
                h += '<div style="text-align:center;color:#D1D5DB;font-size:11px;padding:4px 0;">드래그하여 배치</div>';
            }
            h += '</div>';
            h += `<div style="padding:3px 8px;background:#F9FAFB;border-top:1px solid #F3F4F6;font-size:11px;color:#9CA3AF;">${total}명</div>`;
            h += '</div>';
            if (visibleChildren.length > 0) {
                h += `<div style="width:1px;height:24px;background:#D1D5DB;"></div>`;
            }
            h += this.buildChildrenHtml(dept.id, level + 1);
            h += '</div>';
            return h;
        },

        buildEmpHtml(emp, isLarge) {
            const color = this.getAvatarColor(emp.display_name);
            const initial = (emp.display_name || '?').charAt(0);
            const pos = emp.position_label || '';
            const name = this.esc(emp.display_name || '');
            const label = pos ? `${this.esc(pos)} ${name}` : name;
            if (isLarge) {
                return `<div class="employee-card" data-employee-id="${emp.id}" style="display:flex;align-items:center;gap:8px;padding:6px 10px;border:1px solid #E5E7EB;border-radius:8px;background:#fff;cursor:grab;">
                    <div style="width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:11px;font-weight:700;background:${color};flex-shrink:0;">${initial}</div>
                    <div><p style="font-size:13px;font-weight:500;color:#1F2937;white-space:nowrap;">${label}</p></div></div>`;
            }
            return `<div class="employee-card" data-employee-id="${emp.id}" style="display:flex;align-items:center;gap:4px;padding:3px 6px;margin-bottom:2px;background:#F9FAFB;border-radius:6px;cursor:grab;">
                <div style="width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:8px;font-weight:700;background:${color};flex-shrink:0;">${initial}</div>
                <span style="font-size:11px;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:110px;">${label}</span>
                <button data-action="unassign" data-emp-id="${emp.id}" style="margin-left:auto;color:#D1D5DB;border:none;background:none;cursor:pointer;padding:0;flex-shrink:0;" title="미배치"><i class="ri-close-line" style="font-size:11px;"></i></button></div>`;
        },

        // ===== SortableJS =====
        initDeptSortables() {
            document.querySelectorAll('.org-children').forEach(el => {
                const inst = new Sortable(el, {
                    group: 'departments', handle: '.dept-drag-handle', animation: 200,
                    fallbackOnBody: true, swapThreshold: 0.65, draggable: '.org-node-wrap', emptyInsertThreshold: 20,
                    onStart: () => document.body.classList.add('dept-dragging'),
                    onEnd: () => document.body.classList.remove('dept-dragging'),
                    onMove: (evt) => {
                        const dragId = parseInt(evt.dragged.dataset.deptId);
                        const toPid = evt.to.dataset.parentId ? parseInt(evt.to.dataset.parentId) : null;
                        if (toPid === dragId || this.isDescendant(dragId, toPid)) return false;
                    },
                    onAdd: (evt) => this.handleDeptMove(evt),
                    onUpdate: (evt) => this.handleDeptReorder(evt),
                });
                this.deptSortables.push(inst);
            });
            document.querySelectorAll('.org-drop-target').forEach(el => {
                const inst = new Sortable(el, {
                    group: 'departments', animation: 200, draggable: '.org-node-wrap', emptyInsertThreshold: 30,
                    onStart: () => document.body.classList.add('dept-dragging'),
                    onEnd: () => document.body.classList.remove('dept-dragging'),
                    onMove: (evt) => {
                        const dragId = parseInt(evt.dragged.dataset.deptId);
                        const toPid = parseInt(el.dataset.parentId);
                        if (toPid === dragId || this.isDescendant(dragId, toPid)) return false;
                    },
                    onAdd: (evt) => this.handleDeptMove(evt),
                });
                this.deptSortables.push(inst);
            });
        },

        initEmpSortables() {
            document.querySelectorAll('.emp-zone').forEach(el => {
                const inst = new Sortable(el, {
                    group: 'employees', animation: 150,
                    ghostClass: 'sortable-ghost', dragClass: 'sortable-drag',
                    onAdd: (evt) => {
                        const empId = parseInt(evt.item.dataset.employeeId);
                        const deptId = parseInt(evt.to.dataset.departmentId);
                        evt.item.remove();
                        const emp = this.employees.find(e => e.id === empId);
                        if (emp) emp.department_id = deptId;
                        this.renderTree(); this.renderUnassigned();
                        this.saveAssignment(empId, deptId);
                    },
                });
                this.empSortables.push(inst);
            });
        },

        handleDeptMove(evt) {
            const deptId = parseInt(evt.item.dataset.deptId);
            const newParentId = evt.to.dataset.parentId ? parseInt(evt.to.dataset.parentId) : null;
            const dept = this.departments.find(d => d.id === deptId);
            if (dept) dept.parent_id = newParentId;
            const orders = this.collectSortOrders(evt.to, newParentId);
            if (evt.from !== evt.to) {
                const oldPid = evt.from.dataset.parentId ? parseInt(evt.from.dataset.parentId) : null;
                orders.push(...this.collectSortOrders(evt.from, oldPid));
            }
            orders.forEach(o => {
                const d = this.departments.find(dd => dd.id === o.id);
                if (d) { d.parent_id = o.parent_id; d.sort_order = o.sort_order; }
            });
            this.renderTree(); this.renderUnassigned();
            this.saveDeptOrder(orders);
        },

        handleDeptReorder(evt) {
            const parentId = evt.from.dataset.parentId ? parseInt(evt.from.dataset.parentId) : null;
            const orders = this.collectSortOrders(evt.from, parentId);
            orders.forEach(o => {
                const d = this.departments.find(dd => dd.id === o.id);
                if (d) d.sort_order = o.sort_order;
            });
            this.renderTree();
            this.saveDeptOrder(orders);
        },

        collectSortOrders(container, parentId) {
            const items = container.querySelectorAll(':scope > .org-node-wrap');
            return [...items].map((el, idx) => ({
                id: parseInt(el.dataset.deptId), parent_id: parentId, sort_order: idx + 1,
            }));
        },

        handleClick(e) {
            const btn = e.target.closest('[data-action]');
            if (!btn) {
                if (this.dblClickDept !== null) { this.dblClickDept = null; this.renderTree(); }
                return;
            }
            if (btn.dataset.action === 'unassign') {
                e.preventDefault();
                this.unassignEmployee(parseInt(btn.dataset.empId));
            } else if (btn.dataset.action === 'hide-dept') {
                e.preventDefault(); e.stopPropagation();
                this.hideDept(parseInt(btn.dataset.deptId));
            } else if (btn.dataset.action === 'restore-dept') {
                e.preventDefault();
                this.restoreDept(parseInt(btn.dataset.deptId));
            }
        },

        handleDblClick(e) {
            const header = e.target.closest('[data-action="dept-dblclick"]');
            if (!header) return;
            e.preventDefault();
            const deptId = parseInt(header.dataset.deptId);
            this.dblClickDept = this.dblClickDept === deptId ? null : deptId;
            this.renderTree();
        },

        isDeptHidden(deptId) {
            if (this.hiddenDepts.has(deptId)) return true;
            const dept = this.departments.find(d => d.id === deptId);
            return dept && dept.parent_id ? this.isDeptHidden(dept.parent_id) : false;
        },

        hideDept(deptId) {
            this.hiddenDepts.add(deptId);
            this.dblClickDept = null;
            this.renderTree();
            this.saveHideState(deptId, true);
        },

        restoreDept(deptId) {
            this.hiddenDepts.delete(deptId);
            this.renderTree();
            this.saveHideState(deptId, false);
        },

        async saveHideState(deptId, hidden) {
            this.saving = true;
            try {
                await fetch('{{ route("rd.org-chart.toggle-hide") }}', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, 'Accept': 'application/json' },
                    body: JSON.stringify({ department_id: deptId, hidden }),
                });
            } catch (e) { console.error('숨기기 저장 실패:', e); }
            finally { this.saving = false; }
        },

        get hiddenDeptList() {
            return this.departments.filter(d => this.hiddenDepts.has(d.id));
        },

        async saveAssignment(empId, deptId) {
            this.saving = true;
            try {
                const url = deptId ? '{{ route("rd.org-chart.assign") }}' : '{{ route("rd.org-chart.unassign") }}';
                const body = deptId ? { employee_id: empId, department_id: deptId } : { employee_id: empId };
                await fetch(url, { method:'POST', headers:{'Content-Type':'application/json','X-CSRF-TOKEN':document.querySelector('meta[name="csrf-token"]').content,'Accept':'application/json'}, body:JSON.stringify(body) });
            } catch(e) { console.error('저장 실패:', e); }
            finally { this.saving = false; }
        },

        async saveDeptOrder(orders) {
            this.saving = true;
            try {
                await fetch('{{ route("rd.org-chart.reorder-depts") }}', { method:'POST', headers:{'Content-Type':'application/json','X-CSRF-TOKEN':document.querySelector('meta[name="csrf-token"]').content,'Accept':'application/json'}, body:JSON.stringify({orders}) });
                if (typeof showToast === 'function') showToast('부서 순서가 저장되었습니다.', 'success');
            } catch(e) { console.error('부서 순서 저장 실패:', e); }
            finally { this.saving = false; }
        },

        async unassignEmployee(empId) {
            const emp = this.employees.find(e => e.id === empId);
            if (emp) emp.department_id = null;
            this.renderTree(); this.renderUnassigned();
            await this.saveAssignment(empId, null);
        },
    };
}
</script>
@endpush
