/**
 * TreeView.js - design.html tree-node + API tree-view for SharePoint
 * tree-node: design.html style (nested data, used by Sitecore)
 * tree-view + api-tree-node: API fetch on expand (used by SharePoint)
 */

var WORDPRESS_TREE_PAGE_SIZE = 100;
var WP_LOAD_MORE_PREFIX = '__wp_load_more__|';

/** Umbraco: DB/API can return duplicate folders/items; keep first occurrence per id for stable UI. */
function dedupeTreeNodesById(nodes) {
    if (!nodes || !nodes.length) return [];
    var seen = {};
    var out = [];
    for (var i = 0; i < nodes.length; i++) {
        var n = nodes[i];
        var id = n.id != null ? n.id : n.Id;
        var key = id != null && id !== '' ? String(id) : '__noid_' + i;
        if (seen[key]) continue;
        seen[key] = true;
        out.push(n);
    }
    return out;
}

// Tree Node - from design.html (nested children, in-memory)
Vue.component('tree-node', {
    name: 'tree-node',
    props: {
        node: { type: Object, required: true },
        depth: { type: Number, default: 0 },
        selectedId: { type: [String, Number], default: null },
        excludedIds: { type: Array, default: () => [] },
        includedIds: { type: Array, default: () => [] },
        ancestorExcluded: { type: Boolean, default: false },
        ancestorIncluded: { type: Boolean, default: false }
    },
    computed: {
        iconClass() {
            var m = {
                site: 'bi bi-globe2 site',
                folder: 'bi bi-folder-fill folder',
                page: 'bi bi-file-earmark-richtext page',
                component: 'bi bi-puzzle-fill component',
                media: 'bi bi-image media',
                data: 'bi bi-database data'
            };
            return m[this.node.type] || 'bi bi-file-earmark page';
        },
        isExcluded() {
            if (this.ancestorExcluded) return true;
            if (!this.excludedIds || !this.excludedIds.length) return this.node.excluded;
            return this.excludedIds.indexOf(this.node.id) !== -1 || this.node.excluded;
        },
        isIncluded() {
            if (this.ancestorIncluded) return true;
            if (!this.includedIds || !this.includedIds.length) return false;
            return this.includedIds.indexOf(this.node.id) !== -1;
        }
    },
    methods: {
        onSelect() {
            this.$emit('select', this.node);
        },
        onToggle(e) {
            e.stopPropagation();
            this.$emit('toggle', this.node);
        },
        onChildSelect(node) {
            this.$emit('select', node);
        },
        onChildToggle(node) {
            this.$emit('toggle', node);
        }
    },
    template: `
        <div class="tree-node" v-show="node.visible !== false">
            <div class="tree-row"
                :class="{ selected: selectedId === node.id, excluded: isExcluded, 'included-via-mark': isIncluded && !isExcluded }"
                :style="{ paddingLeft: (depth * 16 + 12) + 'px' }"
                @click="onSelect">
                <span class="tree-toggle" :class="{ open: node.expanded }"
                    v-if="node.children && node.children.length"
                    @click.stop="onToggle">
                    <i class="bi bi-chevron-right"></i>
                </span>
                <span v-else style="width: 18px; flex-shrink: 0;"></span>
                <i class="tree-icon" :class="iconClass"></i>
                <span class="tree-label">{{ node.name }}</span>
                <span class="tree-badges">
                    <span v-if="node.notes" class="tree-badge-note" title="Has notes"></span>
                    <span v-if="isIncluded && !isExcluded" class="tree-badge-selected">SELECT</span>
                    <span v-if="isExcluded" class="tree-badge-excluded">SKIP</span>
                </span>
            </div>
            <div class="tree-children" v-if="node.expanded && node.children">
                <tree-node v-for="child in node.children" :key="child.id"
                    :node="child" :depth="depth + 1" :selected-id="selectedId"
                    :excluded-ids="excludedIds"
                    :included-ids="includedIds"
                    :ancestor-excluded="isExcluded"
                    :ancestor-included="isIncluded"
                    @select="onChildSelect"
                    @toggle="onChildToggle">
                </tree-node>
            </div>
        </div>
    `
});

// API Tree Node - for SharePoint (fetches children on expand)
Vue.component('api-tree-node', {
    name: 'api-tree-node',
    props: {
        node: { type: Object, required: true },
        expanded: { type: Object, required: true },
        nodeData: { type: Object, required: true },
        platformType: { type: String },
        connection: { type: Object, required: true },
        selectedNode: { type: Object, default: null },
        excludedIds: { type: Array, default: () => [] },
        includedIds: { type: Array, default: () => [] },
        ancestorExcluded: { type: Boolean, default: false },
        ancestorIncluded: { type: Boolean, default: false },
        depth: { type: Number, default: 0 },
        nodesEndpoint: { type: String, default: '' },
        /** Node ids (source tree) that have at least one saved import mapping */
        importNodeIds: { type: Array, default: () => [] }
    },
    methods: {
        isLoadMoreNode(node) {
            var id = node && (node.id != null ? node.id : node.Id);
            return node && (node.type === 'LoadMore' || (id && String(id).indexOf(WP_LOAD_MORE_PREFIX) === 0));
        },
        makeLoadMoreNode(parentId, loadedCount, totalCount) {
            var remaining = Math.max(0, (totalCount || 0) - loadedCount);
            return {
                id: WP_LOAD_MORE_PREFIX + parentId + '|' + loadedCount,
                name: 'Load more (' + loadedCount + ' of ' + totalCount + ', ' + remaining + ' remaining)…',
                type: 'LoadMore',
                contentType: 'LoadMore',
                hasChildren: false
            };
        },
        stripLoadMoreNodes(list) {
            if (!list || !list.length) return [];
            return list.filter(function(c) { return c.type !== 'LoadMore' && String(c.id || '').indexOf(WP_LOAD_MORE_PREFIX) !== 0; });
        },
        async toggleNode(node, e) {
            if (e) e.stopPropagation();
            if (!this.expanded[node.id]) {
                try {
                    var page = await this.fetchNodes(node.id, 0);
                    var children = this.finalizeWordPressChildren(node.id, page);
                    this.$set(this.nodeData, node.id, children);
                    this.$set(this.expanded, node.id, true);
                } catch (error) {
                    console.error('Error expanding node:', error);
                    if (typeof Helpers !== 'undefined') Helpers.showNotification(error.message, 'error');
                }
            } else {
                this.$set(this.expanded, node.id, false);
            }
        },
        finalizeWordPressChildren(parentId, page) {
            if (this.platformType !== 'WordPress' || !page || page.items == null) {
                return Array.isArray(page) ? page : (page && page.items) || [];
            }
            var children = page.items.slice();
            if (page.hasMore) {
                children.push(this.makeLoadMoreNode(parentId, page.skip + children.length, page.totalCount));
            }
            return children;
        },
        async loadMoreNodes(loadMoreNode) {
            var id = String(loadMoreNode.id || '');
            var parts = id.split('|');
            if (parts.length < 3) return;
            var parentId = parts[1];
            var skip = parseInt(parts[2], 10) || 0;
            try {
                var page = await this.fetchNodes(parentId, skip);
                var existing = this.stripLoadMoreNodes(this.nodeData[parentId] || []);
                var newItems = (page && page.items) ? page.items : [];
                var merged = existing.concat(newItems);
                if (page && page.hasMore) {
                    merged.push(this.makeLoadMoreNode(parentId, skip + newItems.length, page.totalCount));
                }
                this.$set(this.nodeData, parentId, merged);
            } catch (error) {
                console.error('Error loading more nodes:', error);
                if (typeof Helpers !== 'undefined') Helpers.showNotification(error.message, 'error');
            }
        },
        async fetchNodes(parentId, skip) {
            if (!this.connection) throw new Error('Please connect first!');
            var connId = this.connection.id || this.connection.Id;
            var url;
            if (this.nodesEndpoint) {
                url = this.nodesEndpoint + '?parentId=' + encodeURIComponent(parentId || '') + '&connectionId=' + encodeURIComponent(connId);
            } else {
                url = '/api/ContentTree/GetNodes?parentId=' + encodeURIComponent(parentId || '') + '&connectionId=' + encodeURIComponent(connId) + '&platform=' + encodeURIComponent(this.platformType || '');
            }
            if (this.platformType === 'WordPress') {
                url += '&skip=' + encodeURIComponent(skip || 0) + '&take=' + encodeURIComponent(WORDPRESS_TREE_PAGE_SIZE);
            }
            var response = await fetch(url);
            if (!response.ok) throw new Error('Failed to fetch nodes');
            var data = await response.json();
            if (this.platformType === 'WordPress' && data && !Array.isArray(data) && Array.isArray(data.items)) {
                return {
                    items: data.items,
                    hasMore: !!data.hasMore,
                    totalCount: data.totalCount || 0,
                    skip: data.skip || 0,
                    take: data.take || WORDPRESS_TREE_PAGE_SIZE
                };
            }
            var list = Array.isArray(data) ? data : [];
            if (this.platformType === 'Umbraco') {
                list = dedupeTreeNodesById(list);
            }
            if (this.platformType === 'Custom') {
                list = dedupeTreeNodesById(list);
            }
            return list;
        },
        selectNode(node) {
            if (this.isLoadMoreNode(node)) {
                this.loadMoreNodes(node);
                return;
            }
            this.$emit('node-selected', node);
        }
    },
    computed: {
        isSelected: function() {
            return this.selectedNode && this.selectedNode.id === this.node.id;
        },
        isExcluded: function() {
            if (this.ancestorExcluded) return true;
            if (!this.excludedIds) return false;
            var nodeId = this.node.id || this.node.Id;
            if (this.excludedIds instanceof Set) return this.excludedIds.has(nodeId);
            return Array.isArray(this.excludedIds) && this.excludedIds.indexOf(nodeId) !== -1;
        },
        isIncluded: function() {
            if (this.ancestorIncluded) return true;
            if (!this.includedIds) return false;
            var nodeId = this.node.id || this.node.Id;
            if (this.includedIds instanceof Set) return this.includedIds.has(nodeId);
            return Array.isArray(this.includedIds) && this.includedIds.indexOf(nodeId) !== -1;
        },
        iconClass: function() {
            if (this.isLoadMoreNode(this.node)) {
                return 'bi bi-arrow-down-circle tree-icon load-more';
            }
            // Sitefinity target tree: Site, Module, Provider
            if (this.nodesEndpoint) {
                var t = (this.node.type || this.node.contentType || '').toLowerCase();
                if (t === 'site') return 'bi bi-globe2 tree-icon site';
                if (t === 'module') return 'bi bi-puzzle tree-icon component';
                if (t === 'provider') return 'bi bi-database tree-icon data';
                return 'bi bi-folder-fill tree-icon folder';
            }
            // Sitecore: Inconsistent content node - bug icon, red
            var ghostId = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
            if ((this.node.id || this.node.Id) === ghostId) return 'bi bi-bug tree-icon tree-icon-ghost';
            // Sitecore: use isSite, hasRenderings, hasChildren from API
            if (this.platformType === 'Sitecore') {
                if (this.node.isSite) return 'bi bi-globe2 tree-icon site';
                if (this.node.hasRenderings) return 'bi bi-file-earmark-richtext tree-icon page';
                if (this.node.hasChildren) return 'bi bi-folder-fill tree-icon folder';
                return 'bi bi-file-earmark tree-icon item';
            }
            if (this.platformType === 'SitefinitySource') {
                var sfType = (this.node.type || this.node.contentType || '').toLowerCase();
                if (sfType === 'site') return 'bi bi-globe2 tree-icon site';
                if (sfType === 'pages') return 'bi bi-files tree-icon folder';
                if (sfType === 'module') return 'bi bi-puzzle tree-icon component';
                if (sfType === 'provider') return 'bi bi-database tree-icon data';
                if (sfType === 'page') return 'bi bi-file-earmark-richtext tree-icon page';
                if (sfType === 'widget') return 'bi bi-puzzle tree-icon component';
                if (sfType === 'contentitem') return 'bi bi-file-earmark tree-icon item';
                return 'bi bi-folder-fill tree-icon folder';
            }
            if (this.platformType === 'Custom') {
                var typeLower = (this.node.type || '').toLowerCase();
                var tmplLower = (this.node.contentType || this.node.template || this.node.templateName || '').toLowerCase();
                if (typeLower === 'folder' || tmplLower === 'jsonarray' || tmplLower === 'jsonobject') return 'bi bi-folder-fill tree-icon folder';
                if (typeLower === 'item' || tmplLower === 'jsonitem') return 'bi bi-file-earmark tree-icon page';
                if (tmplLower === 'jsonfile') {
                    if (this.node.hasChildren) return 'bi bi-folder-fill tree-icon folder';
                    return 'bi bi-file-earmark-code tree-icon page';
                }
                return 'bi bi-file-earmark-text tree-icon page';
            }
            // SharePoint / other: fallback to name/type heuristics
            var t = (this.node.type || this.node.name || '').toLowerCase();
            if (t.indexOf('media') !== -1 || t.indexOf('image') !== -1) return 'bi bi-image tree-icon media';
            if (t.indexOf('template') !== -1 || t.indexOf('folder') !== -1) return 'bi bi-folder-fill tree-icon folder';
            if (this.node.hasChildren) return 'bi bi-folder-fill tree-icon folder';
            return 'bi bi-file-earmark tree-icon page';
        },
        hasImportMapping: function() {
            var ids = this.importNodeIds;
            if (!ids || !ids.length) return false;
            var nid = String(this.node.id != null ? this.node.id : (this.node.Id != null ? this.node.Id : ''));
            for (var i = 0; i < ids.length; i++) {
                if (String(ids[i]) === nid) return true;
            }
            return false;
        }
    },
    template: [
        '<li class="tree-node">',
        '  <div class="tree-row"',
        '    :class="{ selected: isSelected, excluded: isExcluded, \'included-via-mark\': isIncluded && !isExcluded, \'tree-row-load-more\': isLoadMoreNode(node) }"',
        '    :style="{ paddingLeft: (depth * 16 + 12) + \'px\' }"',
        '    @click="selectNode(node)">',
        '    <span class="tree-toggle" :class="{ open: expanded[node.id] }"',
        '      v-if="node.hasChildren && !isLoadMoreNode(node)"',
        '      @click.stop="toggleNode(node, $event)">',
        '      <i class="bi bi-chevron-right"></i>',
        '    </span>',
        '    <span v-else class="tree-toggle-placeholder"></span>',
        '    <i class="tree-icon" :class="iconClass"></i>',
        '    <span class="tree-label">{{ node.name }}</span>',
        '    <span class="tree-badges">',
        '      <span v-if="hasImportMapping" class="tree-badge-import" title="Has saved import mapping">IMPORT</span>',
        '      <span v-if="isIncluded && !isExcluded" class="tree-badge-selected">SELECT</span>',
        '      <span v-if="isExcluded" class="tree-badge-excluded">SKIP</span>',
        '    </span>',
        '  </div>',
        '  <div v-if="expanded[node.id] && node.hasChildren" class="tree-children">',
        '    <ul class="list-unstyled">',
        '      <api-tree-node',
        '        v-for="child in nodeData[node.id]"',
        '        :key="child.id"',
        '        :node="child"',
        '        :expanded="expanded"',
        '        :nodeData="nodeData"',
        '        :platformType="platformType"',
        '        :connection="connection"',
        '        :selectedNode="selectedNode"',
        '        :excludedIds="excludedIds"',
        '        :includedIds="includedIds"',
        '        :ancestorExcluded="isExcluded"',
        '        :ancestorIncluded="isIncluded"',
        '        :depth="depth + 1"',
        '        :nodesEndpoint="nodesEndpoint"',
        '        :import-node-ids="importNodeIds"',
        '        @node-selected="$emit(\'node-selected\', $event)"',
        '      ></api-tree-node>',
        '    </ul>',
        '  </div>',
        '</li>'
    ].join('')
});

// Tree View - for SharePoint (API fetch, uses api-tree-node)
Vue.component('tree-view', {
    props: {
        items: { type: Array, default: function() { return []; } },
        connection: { type: Object, required: true },
        selectedNode: { type: Object, default: null },
        platformType: { type: String, default: 'Sitecore' },
        excludedIds: { type: Array, default: function() { return []; } },
        includedIds: { type: Array, default: function() { return []; } },
        searchFilter: { type: String, default: '' },
        initialNodeData: { type: Object, default: function() { return {}; } },
        initialExpanded: { type: Object, default: function() { return {}; } },
        nodesEndpoint: { type: String, default: '' },
        importNodeIds: { type: Array, default: function() { return []; } }
    },
    data: function() {
        return {
            expanded: {},
            nodeData: {}
        };
    },
    computed: {
        excludedSet: function() {
            var ids = this.excludedIds;
            if (!ids) return [];
            if (ids instanceof Set) return [...ids];
            return Array.isArray(ids) ? ids : [];
        },
        includedSet: function() {
            var ids = this.includedIds;
            if (!ids) return [];
            if (ids instanceof Set) return [...ids];
            return Array.isArray(ids) ? ids : [];
        },
        rootNodes: function() {
            var items = this.items || [];
            if (this.platformType === 'Umbraco') {
                items = dedupeTreeNodesById(items);
            }
            if (this.platformType === 'Custom') {
                items = dedupeTreeNodesById(items);
            }
            var q = (this.searchFilter || '').trim().toLowerCase();
            if (!q) return items;
            return items.filter(function(n) { return (n.name || '').toLowerCase().indexOf(q) !== -1; });
        }
    },
    mounted: function() {
        if (this.initialExpanded && Object.keys(this.initialExpanded).length) {
            this.expanded = Object.assign({}, this.initialExpanded);
        }
        if (this.initialNodeData && Object.keys(this.initialNodeData).length) {
            this.nodeData = Object.assign({}, this.initialNodeData);
        }
    },
    watch: {
        items: function(val) {
            if (val && val.length) {
                this.expanded = Object.assign({}, this.initialExpanded);
                this.nodeData = Object.assign({}, this.initialNodeData);
            } else {
                this.expanded = {};
                this.nodeData = {};
            }
        },
        initialNodeData: function(val) {
            if (val && Object.keys(val).length) {
                this.nodeData = Object.assign({}, val);
            }
        },
        initialExpanded: function(val) {
            if (val && Object.keys(val).length) {
                this.expanded = Object.assign({}, val);
            }
        }
    },
    methods: {
        onNodeSelected: function(node) {
            this.$emit('node-selected', node);
        }
    },
    template: [
        '<div class="tree-view">',
        '  <ul class="tree-root list-unstyled">',
        '    <api-tree-node',
        '      v-for="node in rootNodes"',
        '      :key="node.id"',
        '      :node="node"',
        '      :expanded="expanded"',
        '      :nodeData="nodeData"',
        '      :platformType="platformType"',
        '      :connection="connection"',
        '      :selectedNode="selectedNode"',
        '      :excludedIds="excludedSet"',
        '      :includedIds="includedSet"',
        '      :ancestorExcluded="false"',
        '      :ancestorIncluded="false"',
        '      :depth="0"',
        '      :nodesEndpoint="nodesEndpoint"',
        '      :import-node-ids="importNodeIds"',
        '      @node-selected="onNodeSelected"',
        '    ></api-tree-node>',
        '  </ul>',
        '</div>'
    ].join('')
});
