<template>
    <li :class="cx('tree')" role="treeitem" :data-level="level" :aria-level="level" :aria-expanded="isCollapsible ? !collapsed : undefined" :aria-selected="selectionMode ? selected : undefined" v-bind="ptm('tree')">
        <div
            :class="[cx('node'), node.styleClass]"
            :style="node.style"
            :tabindex="nodeSelectable ? 0 : -1"
            :data-selectable="nodeSelectable ? '' : undefined"
            :data-collapsible="isCollapsible ? '' : undefined"
            :data-selected="selected ? '' : undefined"
            :data-partial-selected="partialSelected ? '' : undefined"
            :data-collapsed="collapsed ? '' : undefined"
            @click="onNodeClick"
            @keydown="onNodeKeydown"
            v-bind="ptm('node')"
        >
            <div :class="cx('content')" v-bind="ptm('content')">
                <Checkbox v-if="checkboxMode" :defaultValue="selected" :binary="true" :indeterminate="partialSelected" size="small" :tabindex="-1" :unstyled="unstyled" :pt="ptm('pcCheckbox')" :data-p-partialchecked="partialSelected">
                    <template #icon="slotProps">
                        <component v-if="templates.checkboxicon" :is="templates.checkboxicon" :checked="slotProps.checked" :partialChecked="partialSelected" :class="slotProps.class" />
                    </template>
                </Checkbox>
                <component
                    v-if="templates.default"
                    :is="templates.default"
                    :node="node"
                    :selected="selected"
                    :partialSelected="partialSelected"
                    :collapsed="collapsed"
                    :toggleSelection="toggleSelection"
                    :icon="node.data && node.data.icon ? resolveIcon(node.data.icon) : undefined"
                    :labelClass="cx('label')"
                />
                <span v-else :class="cx('label')" v-bind="ptm('label')">{{ node.label }}</span>
            </div>
            <button v-if="isCollapsible" type="button" :class="cx('toggle')" :aria-expanded="!collapsed" :aria-label="toggleButtonAriaLabel" @click.stop="onToggle" @keydown="onToggleKeydown" v-bind="ptm('toggle')">
                <span :class="cx('toggleIndicator')" v-bind="ptm('toggleIndicator')">
                    <component v-if="templates.toggleicon" :is="templates.toggleicon" :collapsed="collapsed" />
                    <ChevronDown v-else-if="collapsed" />
                    <ChevronUp v-else />
                </span>
            </button>
        </div>
        <ul v-if="node.children?.length && !collapsed" :class="cx('subtree')" role="group" v-bind="ptm('subtree')">
            <OrganizationChartNode
                v-for="child in node.children"
                :key="child.key"
                :node="child"
                :level="level + 1"
                :templates="templates"
                :selectable="selectable"
                :collapsible="collapsible"
                :selectionMode="selectionMode"
                :selectionKeys="selectionKeys"
                :collapsedKeys="collapsedKeys"
                :pt="pt"
                :unstyled="unstyled"
                @node-click="$emit('node-click', $event)"
                @node-toggle="$emit('node-toggle', $event)"
            />
        </ul>
    </li>
</template>

<script>
import ChevronDown from '@primeicons/vue/chevron-down';
import ChevronUp from '@primeicons/vue/chevron-up';
import { isString } from '@primeuix/utils/object';
import BaseComponent from '@primevue/core/basecomponent';
import Checkbox from 'primevue/checkbox';
import { toRaw } from 'vue';

export default {
    name: 'OrganizationChartNode',
    hostName: 'OrganizationChart',
    extends: BaseComponent,
    emits: ['node-click', 'node-toggle'],
    props: {
        node: {
            type: null,
            default: null
        },
        level: {
            type: Number,
            default: 1
        },
        templates: {
            type: null,
            default: null
        },
        selectable: {
            type: Boolean,
            default: true
        },
        collapsible: {
            type: Boolean,
            default: false
        },
        selectionMode: {
            type: String,
            default: null
        },
        selectionKeys: {
            type: null,
            default: null
        },
        collapsedKeys: {
            type: null,
            default: null
        }
    },
    methods: {
        onNodeClick() {
            if (this.nodeSelectable) {
                this.$emit('node-click', this.node);
            }
        },
        toggleSelection() {
            this.onNodeClick();
        },
        onNodeKeydown(event) {
            if (!this.nodeSelectable) return;

            if (event.key === 'Enter' || event.key === ' ') {
                this.$emit('node-click', this.node);
                event.preventDefault();
                event.stopPropagation();
            }
        },
        onToggle() {
            this.$emit('node-toggle', this.node);
        },
        onToggleKeydown(event) {
            if (event.key === 'Enter' || event.key === ' ') {
                this.$emit('node-toggle', this.node);
                event.preventDefault();
                event.stopPropagation();
            }
        },

        resolveIcon(icon) {
            return isString(icon) ? icon : toRaw(icon);
        },
        isComponentIcon(icon) {
            return !!icon && !isString(icon);
        }
    },
    computed: {
        hasChildren() {
            return this.node?.children?.length > 0;
        },
        isCollapsible() {
            return this.hasChildren && this.collapsible;
        },
        checkboxMode() {
            return this.selectionMode === 'checkbox';
        },
        nodeSelectable() {
            if (!this.selectionMode) return false;

            const nodeFlag = this.node.selectable;

            if (nodeFlag === false) return false;
            if (nodeFlag === true) return true;

            return !!this.selectable;
        },
        selected() {
            if (!this.selectionKeys) return false;

            const selection = this.selectionKeys[this.node.key];

            return this.selectionMode === 'checkbox' ? selection?.checked === true : selection === true;
        },
        partialSelected() {
            return this.selectionMode === 'checkbox' && this.selectionKeys?.[this.node.key]?.partialChecked === true;
        },
        collapsed() {
            if (!this.isCollapsible) return false;

            return this.collapsedKeys?.[this.node.key] === true;
        },
        toggleButtonAriaLabel() {
            const locale = this.$primevue?.config?.locale?.aria;

            return this.collapsed ? (locale?.expand ?? 'Expand') : (locale?.collapse ?? 'Collapse');
        }
    },
    components: {
        Checkbox,
        ChevronDown,
        ChevronUp
    }
};
</script>
