/**
 * Entity Selector - Validation Module
 * Conflict detection and prevention for entity selections
 * @partial _validation.js
 *
 * Features:
 * - Same entity in include & exclude detection
 * - Parent-child conflict detection for tree entities
 * - Redundant selection detection
 * - Error message display
 */

(function($) {
    'use strict';

    window._EntitySelectorMixins = window._EntitySelectorMixins || {};

    window._EntitySelectorMixins.validation = {

        /**
         * Validate a selection before adding it
         * Returns { valid: true } or { valid: false, error: 'message', type: 'conflict_type' }
         *
         * @param {number|string} id - Entity ID to validate
         * @param {string} name - Entity name (for error messages)
         * @param {string} section - 'include' or 'exclude'
         * @param {Object} data - Additional data (parent_id, etc.)
         * @returns {Object} Validation result
         */
        validateSelection: function(id, name, section, data) {
            if (!this.activeGroup) {
                return { valid: true };
            }

            var trans = this.config.trans || {};
            id = parseInt(id, 10);

            var $block = this.$wrapper.find('.target-block[data-block-type="' + this.activeGroup.blockType + '"]');
            var $group = $block.find('.selection-group[data-group-index="' + this.activeGroup.groupIndex + '"]');

            // Get include chips
            var includeIds = this.getChipIds($group.find('.include-picker'));

            // Get all exclude chips (from all exclude rows)
            var excludeIds = [];
            $group.find('.exclude-row').each(function() {
                var $excludePicker = $(this).find('.exclude-picker');
                var ids = [];
                $excludePicker.find('.entity-chip').each(function() {
                    ids.push(parseInt($(this).data('id'), 10));
                });
                excludeIds = excludeIds.concat(ids);
            });

            // 1. Check for same entity in include & exclude
            var conflictResult = this.checkIncludeExcludeConflict(id, name, section, includeIds, excludeIds, trans);
            if (!conflictResult.valid) {
                return conflictResult;
            }

            // 2. Check for redundant selection (already selected in same section)
            var redundantResult = this.checkRedundantSelection(id, name, section, includeIds, excludeIds, trans);
            if (!redundantResult.valid) {
                return redundantResult;
            }

            // 3. Check for parent-child conflicts (only for tree entities)
            var searchEntity = this.activeGroup.searchEntity;
            if (searchEntity === 'categories' || searchEntity === 'cms_categories') {
                var treeResult = this.checkTreeConflicts(id, name, section, data, includeIds, excludeIds, trans);
                if (!treeResult.valid) {
                    return treeResult;
                }
            }

            return { valid: true };
        },

        /**
         * Check if entity is in both include and exclude
         */
        checkIncludeExcludeConflict: function(id, name, section, includeIds, excludeIds, trans) {
            if (section === 'include' && excludeIds.indexOf(id) !== -1) {
                return {
                    valid: false,
                    type: 'include_exclude_conflict',
                    error: (trans.error_in_exclude || '"{name}" is already in the exclude list. Remove it from exclude first.').replace('{name}', name)
                };
            }

            if (section === 'exclude' && includeIds.indexOf(id) !== -1) {
                return {
                    valid: false,
                    type: 'include_exclude_conflict',
                    error: (trans.error_in_include || '"{name}" is already in the include list. Remove it from include first.').replace('{name}', name)
                };
            }

            return { valid: true };
        },

        /**
         * Check for redundant selection (already selected)
         */
        checkRedundantSelection: function(id, name, section, includeIds, excludeIds, trans) {
            if (section === 'include' && includeIds.indexOf(id) !== -1) {
                return {
                    valid: false,
                    type: 'redundant',
                    error: (trans.error_already_selected || '"{name}" is already selected.').replace('{name}', name)
                };
            }

            if (section === 'exclude' && excludeIds.indexOf(id) !== -1) {
                return {
                    valid: false,
                    type: 'redundant',
                    error: (trans.error_already_excluded || '"{name}" is already in an exclude list.').replace('{name}', name)
                };
            }

            return { valid: true };
        },

        /**
         * Check for parent-child conflicts in tree entities
         */
        checkTreeConflicts: function(id, name, section, data, includeIds, excludeIds, trans) {
            // Need tree data for parent-child lookups
            if (!this.treeFlatData) {
                return { valid: true };
            }

            var parentId = data && data.parentId ? parseInt(data.parentId, 10) : null;

            // Build lookup for quick access
            var lookup = {};
            this.treeFlatData.forEach(function(item) {
                lookup[parseInt(item.id, 10)] = item;
            });

            // Get all ancestor IDs
            var ancestorIds = this.getAncestorIds(id, lookup);

            // Get all descendant IDs
            var descendantIds = this.getDescendantIds(id, lookup);

            if (section === 'include') {
                // Check if any ancestor is excluded
                for (var i = 0; i < ancestorIds.length; i++) {
                    if (excludeIds.indexOf(ancestorIds[i]) !== -1) {
                        var ancestorName = lookup[ancestorIds[i]] ? lookup[ancestorIds[i]].name : 'Parent';
                        return {
                            valid: false,
                            type: 'parent_excluded',
                            error: (trans.error_parent_excluded || 'Cannot include "{name}" because its parent "{parent}" is excluded.').replace('{name}', name).replace('{parent}', ancestorName)
                        };
                    }
                }

                // Check if any descendant is excluded
                for (var j = 0; j < descendantIds.length; j++) {
                    if (excludeIds.indexOf(descendantIds[j]) !== -1) {
                        var descendantName = lookup[descendantIds[j]] ? lookup[descendantIds[j]].name : 'Child';
                        return {
                            valid: false,
                            type: 'child_excluded',
                            error: (trans.error_child_excluded || 'Cannot include "{name}" because its child "{child}" is excluded. Remove the child from exclude first.').replace('{name}', name).replace('{child}', descendantName)
                        };
                    }
                }
            }

            if (section === 'exclude') {
                // Check if any ancestor is included
                for (var k = 0; k < ancestorIds.length; k++) {
                    if (includeIds.indexOf(ancestorIds[k]) !== -1) {
                        var parentName = lookup[ancestorIds[k]] ? lookup[ancestorIds[k]].name : 'Parent';
                        return {
                            valid: false,
                            type: 'parent_included',
                            error: (trans.error_parent_included || 'Cannot exclude "{name}" because its parent "{parent}" is included. This would create a contradiction.').replace('{name}', name).replace('{parent}', parentName)
                        };
                    }
                }

                // Check if any descendant is included (warning about implicit exclusion)
                var includedDescendants = [];
                for (var m = 0; m < descendantIds.length; m++) {
                    if (includeIds.indexOf(descendantIds[m]) !== -1) {
                        var childName = lookup[descendantIds[m]] ? lookup[descendantIds[m]].name : 'Child';
                        includedDescendants.push(childName);
                    }
                }

                if (includedDescendants.length > 0) {
                    return {
                        valid: false,
                        type: 'children_included',
                        error: (trans.error_children_included || 'Cannot exclude "{name}" because its children ({children}) are included. Remove them from include first.').replace('{name}', name).replace('{children}', includedDescendants.slice(0, 3).join(', ') + (includedDescendants.length > 3 ? '...' : ''))
                    };
                }
            }

            return { valid: true };
        },

        /**
         * Get all ancestor IDs for a given item
         */
        getAncestorIds: function(id, lookup) {
            var ancestors = [];
            var current = lookup[id];

            while (current && current.parent_id) {
                var parentId = parseInt(current.parent_id, 10);
                if (parentId && lookup[parentId]) {
                    ancestors.push(parentId);
                    current = lookup[parentId];
                } else {
                    break;
                }
            }

            return ancestors;
        },

        /**
         * Get all descendant IDs for a given item
         */
        getDescendantIds: function(id, lookup) {
            var descendants = [];
            var self = this;

            // Find direct children
            Object.keys(lookup).forEach(function(key) {
                var item = lookup[key];
                if (parseInt(item.parent_id, 10) === id) {
                    var childId = parseInt(item.id, 10);
                    descendants.push(childId);
                    // Recursively get children's descendants
                    var childDescendants = self.getDescendantIds(childId, lookup);
                    descendants = descendants.concat(childDescendants);
                }
            });

            return descendants;
        },

        /**
         * Get chip IDs from a picker
         */
        getChipIds: function($picker) {
            var ids = [];
            $picker.find('.entity-chip').each(function() {
                ids.push(parseInt($(this).data('id'), 10));
            });
            return ids;
        },

        /**
         * Validate pending selections (for tree view bulk operations)
         * Returns array of invalid items
         */
        validatePendingSelections: function(pendingSelections, section) {
            var self = this;
            var errors = [];

            if (!pendingSelections || !pendingSelections.length) {
                return errors;
            }

            pendingSelections.forEach(function(sel) {
                var result = self.validateSelection(sel.id, sel.name, section, sel.data || {});
                if (!result.valid) {
                    errors.push({
                        id: sel.id,
                        name: sel.name,
                        error: result.error,
                        type: result.type
                    });
                }
            });

            return errors;
        },

        /**
         * Show validation error toast
         */
        showValidationError: function(message) {
            var trans = this.config.trans || {};
            var title = trans.validation_error || 'Selection Conflict';

            // Remove existing toast
            $('.es-validation-toast').remove();

            // Create toast HTML
            var html = '<div class="es-validation-toast">';
            html += '<div class="es-toast-icon">' + this.esIcon('warning') + '</div>';
            html += '<div class="es-toast-content">';
            html += '<div class="es-toast-title">' + this.escapeHtml(title) + '</div>';
            html += '<div class="es-toast-message">' + this.escapeHtml(message) + '</div>';
            html += '</div>';
            html += '<button type="button" class="es-toast-close">' + this.esIcon('close') + '</button>';
            html += '</div>';

            var $toast = $(html);
            $('body').append($toast);

            // Position near dropdown if visible
            if (this.$dropdown && this.$dropdown.hasClass('show')) {
                var dropdownOffset = this.$dropdown.offset();
                $toast.css({
                    position: 'fixed',
                    top: dropdownOffset.top - $toast.outerHeight() - 10,
                    left: dropdownOffset.left,
                    zIndex: 10001
                });
            } else {
                $toast.css({
                    position: 'fixed',
                    top: 20,
                    right: 20,
                    zIndex: 10001
                });
            }

            // Animate in
            $toast.hide().fadeIn(200);

            // Auto-dismiss after 5 seconds
            setTimeout(function() {
                $toast.fadeOut(200, function() {
                    $(this).remove();
                });
            }, 5000);

            // Close button
            $toast.on('click', '.es-toast-close', function() {
                $toast.fadeOut(200, function() {
                    $(this).remove();
                });
            });
        },

        /**
         * Validate and add selection (wrapper that validates before adding)
         * Returns true if added successfully, false if validation failed
         */
        validateAndAddSelection: function($picker, id, name, data, section) {
            var result = this.validateSelection(id, name, section, data || {});

            if (!result.valid) {
                this.showValidationError(result.error);
                return false;
            }

            // Validation passed, add the selection
            this.addSelection($picker, id, name, data);
            return true;
        }
    };

})(jQuery);
