/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-base-to-string */
/* eslint-disable @typescript-eslint/typedef */
// #region Imports
/**
 * @public
 */
export function Attribute(options) {
    return (protoOrDescriptor, name) => name !== undefined
        ? createLegacyProperty(protoOrDescriptor, name, getDefaultOptions(name, options))
        : createStandardProperty(protoOrDescriptor, getDefaultOptions(protoOrDescriptor.key, options));
}
/**
 * @private
 */
function getDefaultOptions(propertyKey, options) {
    options = {
        reflect: true,
        // camelCaseToKebabCase attribute name converter
        attribute: propertyKey.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-')
            .toLowerCase(),
        hasChanged: (value, oldValue) => {
            const hasChanges = oldValue !== value;
            return hasChanges;
        },
        ...options
    };
    switch (options.type) {
        case Array:
            return {
                converter: {
                    fromAttribute: (value) => Array.isArray(value) ? value.split(' ') : value ?? false
                },
                ...options
            };
        case String:
            return {
                converter: {
                    toAttribute: (value) => value ? value : null
                },
                ...options
            };
        case Boolean:
            return {
                converter: {
                    // Mimic standard HTML boolean attributes + support "false" attribute values
                    // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes
                    toAttribute: (value) => value ? '' : null,
                    fromAttribute: (value) => value !== null && value !== undefined && `${value}` !== 'false'
                },
                ...options
            };
        case Date: {
            return {
                // Parse date strings from attributes but do not reflect back into attribute
                converter: {
                    fromAttribute: (value) => new Date(value)
                },
                ...options
            };
        }
        case Object:
        case Number:
        default:
            return {
                ...options
            };
    }
}
/**
 * @private
 */
function createStandardProperty(element, options) {
    // When decorating an accessor, pass it through and add property metadata.
    // Note, the `hasOwnProperty` check in `createProperty` ensures we don't
    // stomp over the user's accessor.
    if (element.kind === 'method' && element.descriptor && !('value' in element.descriptor)) {
        return {
            ...element,
            finisher: (ctor) => {
                createProperty(ctor, element.key, options);
            }
        };
    }
    // createProperty() takes care of defining the property, but we still
    // must return some kind of descriptor, so return a descriptor for an
    // unused prototype field. The finisher calls createProperty().
    return {
        kind: 'field',
        key: Symbol(),
        placement: 'own',
        descriptor: {},
        // store the original key so subsequent decorators have access to it.
        originalKey: element.key,
        initializer() {
            if (typeof element.initializer === 'function') {
                this[element.key] = element.initializer.call(this);
            }
        },
        finisher(ctor) {
            createProperty(ctor, element.key, options);
        }
    };
}
/**
 * @private
 */
function createLegacyProperty(proto, name, options) {
    createProperty(proto.constructor, name, options);
}
/**
 * @private
 */
function createProperty(element, name, options) {
    element.createProperty(name, options);
}
//# sourceMappingURL=AttributeDecorator.js.map