define([
    'jquery',
    'uiComponent',
    'ko'
], function ($, Component, ko) {
    'use strict';

    var SCRIPT_URL = 'https://assets.swiftcomplete.com/js/swiftlookup.js';
    var DEFAULT_PROMPT = 'Type your address, postcode, or what3words';
    var SEARCH_FIELD_ID = 'portalpeople-swiftcomplete-search';
    var CONTROL_KEY = 'portalpeopleSwiftcompleteSearch';
    var FIELD_SELECTORS = {
        street1: ['[name="street[0]"]', '[name="shippingAddress.street.0"]'],
        street2: ['[name="street[1]"]', '[name="shippingAddress.street.1"]'],
        city: ['[name="city"]', '[name="shippingAddress.city"]'],
        postcode: ['[name="postcode"]', '[name="shippingAddress.postcode"]'],
        country: ['[name="country_id"]', '[name="shippingAddress.country_id"]']
    };

    return Component.extend({
        defaults: {
            template: 'PortalPeople_Swiftcomplete/swiftcomplete'
        },

        isEnabled: ko.observable(false),
        isReady: ko.observable(false),
        errorMessage: ko.observable(''),
        inputLabel: ko.observable('Address lookup'),
        inputPlaceholder: ko.observable('Start typing your address, postcode, or what3words'),
        scriptPromise: null,
        pollTimer: null,

        initialize: function () {
            this._super();

            var config = this.getConfig();
            this.isEnabled(!!config.enabled && !!config.apiKey);
            this.inputLabel(config.inputLabel || 'Address lookup');
            this.inputPlaceholder(config.inputPlaceholder || 'Start typing your address, postcode, or what3words');

            if (this.isEnabled()) {
                this.bootstrap();
            }

            return this;
        },

        getConfig: function () {
            if (window.checkoutConfig && window.checkoutConfig.portalpeopleSwiftcomplete) {
                return window.checkoutConfig.portalpeopleSwiftcomplete;
            }

            return {};
        },

        bootstrap: function () {
            var self = this;

            this.ensureScriptLoaded().done(function () {
                self.startPolling();
            }).fail(function () {
                self.errorMessage('Swiftcomplete could not be loaded. Check the API key and CSP allowlist.');
            });
        },

        ensureScriptLoaded: function () {
            var deferred = $.Deferred();
            var existingScript;
            var config = this.getConfig();

            if (window.swiftcomplete && window.swiftcomplete.SwiftLookup) {
                deferred.resolve();
                return deferred.promise();
            }

            if (this.scriptPromise) {
                return this.scriptPromise;
            }

            existingScript = document.querySelector('script[data-swiftcomplete-script="true"]');
            if (existingScript) {
                existingScript.addEventListener('load', function () {
                    deferred.resolve();
                });
                existingScript.addEventListener('error', function () {
                    deferred.reject();
                });
                this.scriptPromise = deferred.promise();
                return this.scriptPromise;
            }

            var script = document.createElement('script');
            script.async = true;
            script.src = config.scriptUrl || SCRIPT_URL;
            script.setAttribute('data-swiftcomplete-script', 'true');
            script.onload = function () {
                deferred.resolve();
            };
            script.onerror = function () {
                deferred.reject();
            };

            document.head.appendChild(script);
            this.scriptPromise = deferred.promise();

            return this.scriptPromise;
        },

        startPolling: function () {
            var self = this;

            if (this.pollTimer) {
                return;
            }

            this.tryInitialize();

            this.pollTimer = window.setInterval(function () {
                if (self.tryInitialize()) {
                    window.clearInterval(self.pollTimer);
                    self.pollTimer = null;
                }
            }, 200);

            window.setTimeout(function () {
                if (!self.isReady()) {
                    window.clearInterval(self.pollTimer);
                    self.pollTimer = null;
                    if (!self.errorMessage()) {
                        self.errorMessage('Swiftcomplete is enabled, but the checkout address fields were not found.');
                    }
                }
            }, 15000);
        },

        tryInitialize: function () {
            if (this.isReady()) {
                return true;
            }

            if (!(window.swiftcomplete && window.swiftcomplete.SwiftLookup)) {
                return false;
            }

            var searchField = document.getElementById(SEARCH_FIELD_ID);
            var fields = this.resolveFields();

            if (!searchField || !fields.street1 || !fields.city || !fields.postcode || !fields.country) {
                return false;
            }

            this.createControl(searchField, fields);
            this.isReady(true);

            return true;
        },

        createControl: function (searchField, fields) {
            var config = this.getConfig();
            var self = this;
            var populateLineFormat = this.buildPopulateLineFormat(fields);
            var initializeControl = function () {
                window.swiftcomplete.controls = window.swiftcomplete.controls || {};
                window.swiftcomplete.controls[CONTROL_KEY] = new window.swiftcomplete.SwiftLookup({
                    field: searchField,
                    key: config.apiKey,
                    searchFor: config.searchFor || 'what3words,address',
                    emptyQueryMode: 'prompt',
                    promptText: config.promptText || DEFAULT_PROMPT,
                    scrollToFieldOnFocus: true,
                    populateLineFormat: populateLineFormat
                });

                if (window.swiftcomplete.controls[CONTROL_KEY].setCountries) {
                    window.swiftcomplete.controls[CONTROL_KEY].setCountries(config.countryCode || 'gb');
                }

                searchField.addEventListener('swiftcomplete:swiftlookup:selected', function () {
                    self.errorMessage('');
                }, false);
            };

            if (window.swiftcomplete.runWhenReady) {
                window.swiftcomplete.runWhenReady(initializeControl);
            } else {
                initializeControl();
            }
        },

        resolveFields: function () {
            return {
                street1: this.findField(FIELD_SELECTORS.street1),
                street2: this.findField(FIELD_SELECTORS.street2),
                city: this.findField(FIELD_SELECTORS.city),
                postcode: this.findField(FIELD_SELECTORS.postcode),
                country: this.findField(FIELD_SELECTORS.country)
            };
        },

        findField: function (selectors) {
            var i;
            var field;

            for (i = 0; i < selectors.length; i += 1) {
                field = document.querySelector(selectors[i]);
                if (field) {
                    return field;
                }
            }

            return null;
        },

        buildPopulateLineFormat: function (fields) {
            var populateLineFormat = [
                {
                    format: 'AddressLine1',
                    field: fields.street1
                },
                {
                    format: 'TertiaryLocality, SecondaryLocality, PrimaryLocality STATEABBREVIATION',
                    field: fields.city
                },
                {
                    format: 'POSTCODE',
                    field: fields.postcode
                },
                {
                    format: 'PrimaryCountry',
                    field: fields.country
                }
            ];

            if (fields.street2) {
                populateLineFormat.splice(1, 0, {
                    format: 'AddressLine2',
                    field: fields.street2
                });
            }

            return populateLineFormat;
        }
    });
});