(function(window, $) {
    if (!window || !$)
        return;

    var CompanyAreaDependency = {
        init: function(formSelector) {
            var $form = $(formSelector || '#ticketForm');

            if (!$form.length)
                return;

            var parentVar = $form.data('company-area-parent-var') || 'CompanySourceTD';
            var childVar = $form.data('company-area-child-var') || 'area';
            var endpoint = $form.data('company-area-endpoint') || 'ajax.php/company-area/children';

            var $parentWrap = this.findFieldWrap($form, parentVar, 'parent');
            var $childWrap = this.findFieldWrap($form, childVar, 'child');

            if (!$parentWrap.length || !$childWrap.length)
                return;

            var $parent = this.findInput($parentWrap);
            var $child = this.findInput($childWrap);

            if (!$parent.length || !$child.length)
                return;

            if (!$child.is('select'))
                return;

            if (!$child.data('companyAreaOriginalOptions')) {
                $child.data('companyAreaOriginalOptions', this.readOptions($child));
            }

            if (typeof $child.data('companyAreaInitialValue') === 'undefined') {
                $child.data('companyAreaInitialValue', $child.val());
            }

            this.ensureErrorBox($childWrap);

            $parent.off('.companyAreaDependency');
            $form.off('.companyAreaDependency');

            var self = this;

            $parent.on('change.companyAreaDependency', function() {
                self.refresh($parent, $child, $childWrap, endpoint, false);
            });

            $form.on('submit.companyAreaDependency', function(e) {
                if ($child.data('companyAreaLoading')) {
                    self.showError($childWrap, 'Espera a que se carguen las áreas disponibles.');
                    e.preventDefault();
                    return false;
                }

                if (!self.validateCurrentSelection($parent, $child, $childWrap, false)) {
                    e.preventDefault();
                    return false;
                }
            });

            this.refresh($parent, $child, $childWrap, endpoint, true);
        },

        findFieldWrap: function($form, fieldVar, role) {
            var $wrap = $form.find('.dynamic-field-wrap[data-company-area-role="' + role + '"]').first();
            if ($wrap.length)
                return $wrap;

            $wrap = $form.find('.dynamic-field-wrap[data-field-var="' + fieldVar + '"]').first();
            if ($wrap.length)
                return $wrap;

            fieldVar = String(fieldVar).toLowerCase();

            return $form.find('.dynamic-field-wrap').filter(function() {
                var varName = String($(this).attr('data-field-var') || '').toLowerCase();
                var formName = String($(this).attr('data-field-form-name') || '').toLowerCase();

                return (
                    varName === fieldVar ||
                    formName === fieldVar ||
                    formName.indexOf('[' + fieldVar + ']') !== -1
                );
            }).first();
        },

        findInput: function($wrap) {
            return $wrap.find('select, input, textarea').first();
        },

        readOptions: function($select) {
            var items = [];
            $select.find('option').each(function() {
                items.push({
                    value: $(this).attr('value'),
                    label: $(this).text()
                });
            });
            return items;
        },

        getSelectedText: function($select) {
            var rawValue = this.normalize($select.val());
            if (!rawValue)
                return '';

            var $selected = $select.find('option:selected').first();
            if (!$selected.length)
                return rawValue;

            return this.normalize($selected.text());
        },

        findOriginalOptionByLabel: function($select, label) {
            label = this.normalize(label);

            var options = $select.data('companyAreaOriginalOptions') || [];
            for (var i = 0; i < options.length; i++) {
                if (this.normalize(options[i].label) === label)
                    return options[i];
            }

            return null;
        },

        findOptionValueByLabel: function($select, label) {
            label = this.normalize(label);

            var found = null;
            $select.find('option').each(function() {
                if ($.trim($(this).text()) === label) {
                    found = $(this).attr('value');
                    return false;
                }
            });

            return found;
        },

        getPlaceholder: function($select) {
            var options = $select.data('companyAreaOriginalOptions') || [];
            for (var i = 0; i < options.length; i++) {
                if (String(options[i].value || '') === '')
                    return options[i].label;
            }
            return '— Seleccionar —';
        },

        setLoading: function($select) {
            $select.data('companyAreaLoading', true);
            $select.empty();
            $select.append($('<option />', {
                value: '',
                text: 'Cargando...'
            }));
        },

        clearLoading: function($select) {
            $select.data('companyAreaLoading', false);
        },

        resetChild: function($select) {
            var placeholder = this.getPlaceholder($select);

            $select.empty();
            $select.append($('<option />', {
                value: '',
                text: placeholder
            }));
            $select.val('');
        },

        populateChild: function($select, items, selectedValue) {
            var self = this;
            var placeholder = this.getPlaceholder($select);

            $select.empty();
            $select.append($('<option />', {
                value: '',
                text: placeholder
            }));

            if ($.isArray(items)) {
                $.each(items, function(_, item) {
                    var label = item && item.label ? item.label : (item && item.value ? item.value : '');
                    var original = self.findOriginalOptionByLabel($select, label);

                    var value = original
                        ? original.value
                        : (item && item.value ? item.value : label);

                    var text = original
                        ? original.label
                        : label;

                    $select.append($('<option />', {
                        value: value,
                        text: text
                    }));
                });
            }

            if (selectedValue) {
                if (this.hasOption($select, selectedValue)) {
                    $select.val(selectedValue);
                } else {
                    var selectedByLabel = this.findOptionValueByLabel($select, selectedValue);
                    if (selectedByLabel !== null && typeof selectedByLabel !== 'undefined')
                        $select.val(selectedByLabel);
                    else
                        $select.val('');
                }
            } else {
                $select.val('');
            }
        },

        hasOption: function($select, value) {
            if (typeof value === 'undefined' || value === null || value === '')
                return false;

            return $select.find('option').filter(function() {
                return String($(this).attr('value')) === String(value);
            }).length > 0;
        },

        hasOptionByLabel: function($select, label) {
            label = this.normalize(label);
            if (!label)
                return false;

            return $select.find('option').filter(function() {
                return $.trim($(this).text()) === label;
            }).length > 0;
        },

        ensureErrorBox: function($wrap) {
            var $error = $wrap.find('.company-area-error');
            if (!$error.length) {
                $error = $('<div class="error company-area-error" style="display:none;"></div>');
                $wrap.append($error);
            }
            return $error;
        },

        showError: function($wrap, message) {
            var $error = this.ensureErrorBox($wrap);
            $error.text(message).show();
        },

        clearError: function($wrap) {
            $wrap.find('.company-area-error').hide().text('');
        },

        normalize: function(value) {
            if (typeof value === 'undefined' || value === null)
                return '';
            return $.trim(String(value));
        },

        refresh: function($parent, $child, $childWrap, endpoint, isInitialLoad) {
            var self = this;
            var parentValue = this.getSelectedText($parent);

            // En cambio manual de empresa/ciudad NO arrastramos el área anterior
            // para no mostrar falso error.
            var selectedValue = isInitialLoad
                ? this.normalize($child.data('companyAreaInitialValue') || $child.val())
                : '';

            this.clearError($childWrap);

            if (!parentValue) {
                this.resetChild($child);
                this.clearLoading($child);
                return;
            }

            this.setLoading($child);

            $.ajax({
                url: endpoint,
                type: 'GET',
                cache: false,
                dataType: 'json',
                data: {
                    parent: parentValue
                },
                success: function(json) {
                    var items = (json && json.items) ? json.items : [];
                    self.populateChild($child, items, selectedValue);
                    self.clearLoading($child);

                    if (isInitialLoad && selectedValue) {
                        var isValidSelection =
                            self.hasOption($child, selectedValue) ||
                            self.hasOptionByLabel($child, selectedValue);

                        if (!isValidSelection) {
                            self.showError($childWrap, 'El área actual no pertenece a la empresa/ciudad/unidad elegida.');
                        } else {
                            self.clearError($childWrap);
                        }
                    } else {
                        self.clearError($childWrap);
                    }
                },
                error: function() {
                    self.resetChild($child);
                    self.clearLoading($child);
                    self.showError($childWrap, 'No fue posible cargar las áreas disponibles.');
                }
            });
        },

        validateCurrentSelection: function($parent, $child, $childWrap, silent) {
            var parentValue = this.getSelectedText($parent);
            var childValue = this.normalize($child.val());

            if (!parentValue || !childValue) {
                this.clearError($childWrap);
                return true;
            }

            if (!this.hasOption($child, childValue)) {
                if (!silent) {
                    this.showError($childWrap, 'El área seleccionada no pertenece a la empresa/ciudad/unidad elegida.');
                }
                return false;
            }

            this.clearError($childWrap);
            return true;
        }
    };

    window.CompanyAreaDependency = CompanyAreaDependency;

})(window, window.jQuery);