package com.atlassian.stash.internal.plugin.web.fragments;

import com.google.common.base.Function;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public enum ClientWebFragmentFieldType {
    STRING(new Function<Element, String>() {
        @Override
        public String apply(Element input) {
            return "'" + StringEscapeUtils.escapeEcmaScript(input.getText().trim()) + "'";
        }
    }),
    TEXT(new Function<Element, String>() {
        @Override
        public String apply(Element input) {
            String fallbackString = "'" + StringEscapeUtils.escapeEcmaScript(input.getText().trim()) + "'";
            String i18nKey = input.attributeValue("i18n_key");
            String transformedString = "null";
            String i18nKeyJsString = "null";

            if (i18nKey != null) {
                i18nKeyJsString = "'" + StringEscapeUtils.escapeEcmaScript(i18nKey) + "'";
                transformedString = "AJS.I18n.getText(" + i18nKeyJsString + ")";
            }

            // We pass the text through WebFragments._formatI18n which will handle the fallbackPattern logic and also
            // allow formatting with arguments from the context.
            return "WebFragments._formatI18n(" + transformedString + ", " + i18nKeyJsString + ", " + fallbackString + ")";
        }
    }),
    NUMBER(new Function<Element, String>() {
        @Override
        public String apply(Element input) {
            String text = input.getText();
            if (!text.matches("^\\d+(\\.\\d+)?$")) {
                throw new NumberFormatException("Attempt to render numeric JS property, but value was not numeric.");
            }
            return text;
        }
    }),
    ARRAY(new Function<Element, String>() {
        @Override
        public String apply(Element input) {
            throw new UnsupportedOperationException("Arrays are not expected to be represented as strings.");
        }
    }),
    OBJECT(new Function<Element, String>() {
        @Override
        public String apply(Element input) {
            throw new UnsupportedOperationException("Objects are not expected to be represented as strings.");
        }
    }),
    JS(new Function<Element, String>() {
        @Override
        public String apply(Element input) {
            String src = input.getText();
            if (StringUtils.isBlank(src)) {
                log.warn("Client fragment fields that are typed JS, like client-context-provider and client-condition, should not be empty. Using 'undefined'.");
                return "undefined";
            }
            return "function(ctx) { return WebFragments._getValue((" + src + "), ctx); }";
        }
    });

    private final Function<Element, String> toJsSrcFn;

    private static final Logger log = LoggerFactory.getLogger(ClientWebFragmentFieldType.class);

    ClientWebFragmentFieldType(Function<Element, String> toJsSrcFn) {
        this.toJsSrcFn = toJsSrcFn;
    }

    public String toJsSrc(Element in) {
        return toJsSrcFn.apply(in);
    }

    public static ClientWebFragmentFieldType fromTypeAttribute(String typeAttr) {
        return fromTypeAttribute(typeAttr, null);
    }

    public static ClientWebFragmentFieldType fromTypeAttribute(String typeAttr, ClientWebFragmentFieldType defaultType) {
        if (typeAttr != null) {
            return ClientWebFragmentFieldType.valueOf(typeAttr.toUpperCase());
        }
        if (defaultType != null) {
            return defaultType;
        }
        throw new IllegalArgumentException("No typeAttr value or defaultType were provided");
    }
}
