import { hyphenate, capitalize, extend } from '@vue/shared';

function createDecl(prop, value, important, raws, source) {
    const decl = {
        type: 'decl',
        prop,
        value: value.toString(),
        raws,
        source,
    };
    if (important) {
        decl.important = true;
    }
    return decl;
}

const backgroundColor = 'backgroundColor';
const backgroundImage = 'backgroundImage';
const transformBackground = (decl) => {
    const { value, important, raws, source } = decl;
    if (/^#?\S+$/.test(value) || /^rgba?(.+)$/.test(value)) {
        return [createDecl(backgroundColor, value, important, raws, source)];
    }
    else if (/^linear-gradient(.+)$/.test(value)) {
        return [createDecl(backgroundImage, value, important, raws, source)];
    }
    return [decl];
};

const borderWidth = 'Width';
const borderStyle = 'Style';
const borderColor = 'Color';
const transformBorder = (decl) => {
    const { prop, value, important, raws, source } = decl;
    const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/);
    const result = [/^[\d\.]+\S*$/, /^(solid|dashed|dotted)$/, /\S+/].map((item) => {
        const index = splitResult.findIndex((str) => item.test(str));
        return index < 0 ? null : splitResult.splice(index, 1)[0];
    });
    if (splitResult.length) {
        return [decl];
    }
    return [
        createDecl(prop + borderWidth, (result[0] || '0').trim(), important, raws, source),
        createDecl(prop + borderStyle, (result[1] || 'solid').trim(), important, raws, source),
        createDecl(prop + borderColor, (result[2] || '#000000').trim(), important, raws, source),
    ];
};

const borderTop = 'borderTop';
const borderRight = 'borderRight';
const borderBottom = 'borderBottom';
const borderLeft = 'borderLeft';
const transformBorderColor = (decl) => {
    const { prop, value, important, raws, source } = decl;
    let property = hyphenate(prop).split('-')[1];
    {
        property = capitalize(property);
    }
    const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/);
    switch (splitResult.length) {
        case 1:
            return [decl];
        case 2:
            splitResult.push(splitResult[0], splitResult[1]);
            break;
        case 3:
            splitResult.push(splitResult[1]);
            break;
    }
    return [
        createDecl(borderTop + property, splitResult[0], important, raws, source),
        createDecl(borderRight + property, splitResult[1], important, raws, source),
        createDecl(borderBottom + property, splitResult[2], important, raws, source),
        createDecl(borderLeft + property, splitResult[3], important, raws, source),
    ];
};

const borderTopLeftRadius = 'borderTopLeftRadius';
const borderTopRightRadius = 'borderTopRightRadius';
const borderBottomRightRadius = 'borderBottomRightRadius';
const borderBottomLeftRadius = 'borderBottomLeftRadius';
const transformBorderRadius = (decl) => {
    const { value, important, raws, source } = decl;
    const splitResult = value.split(/\s+/);
    if (value.includes('/')) {
        return [decl];
    }
    switch (splitResult.length) {
        case 1:
            return [decl];
        case 2:
            splitResult.push(splitResult[0], splitResult[1]);
            break;
        case 3:
            splitResult.push(splitResult[1]);
            break;
    }
    return [
        createDecl(borderTopLeftRadius, splitResult[0], important, raws, source),
        createDecl(borderTopRightRadius, splitResult[1], important, raws, source),
        createDecl(borderBottomRightRadius, splitResult[2], important, raws, source),
        createDecl(borderBottomLeftRadius, splitResult[3], important, raws, source),
    ];
};

const transformBorderStyle = transformBorderColor;

const transformBorderWidth = transformBorderColor;

const flexDirection = 'flexDirection';
const flexWrap = 'flexWrap';
const transformFlexFlow = (decl) => {
    const { value, important, raws, source } = decl;
    const splitResult = value.split(/\s+/);
    const result = [
        /^(column|column-reverse|row|row-reverse)$/,
        /^(nowrap|wrap|wrap-reverse)$/,
    ].map((item) => {
        const index = splitResult.findIndex((str) => item.test(str));
        return index < 0 ? null : splitResult.splice(index, 1)[0];
    });
    if (splitResult.length) {
        return [decl];
    }
    return [
        createDecl(flexDirection, result[0] || 'column', important, raws, source),
        createDecl(flexWrap, result[1] || 'nowrap', important, raws, source),
    ];
};

const top = 'Top';
const right = 'Right';
const bottom = 'Bottom';
const left = 'Left';
const createTransformBox = (type) => {
    return (decl) => {
        const { value, important, raws, source } = decl;
        const splitResult = value.split(/\s+/);
        switch (splitResult.length) {
            case 1:
                splitResult.push(splitResult[0], splitResult[0], splitResult[0]);
                break;
            case 2:
                splitResult.push(splitResult[0], splitResult[1]);
                break;
            case 3:
                splitResult.push(splitResult[1]);
                break;
        }
        return [
            createDecl(type + top, splitResult[0], important, raws, source),
            createDecl(type + right, splitResult[1], important, raws, source),
            createDecl(type + bottom, splitResult[2], important, raws, source),
            createDecl(type + left, splitResult[3], important, raws, source),
        ];
    };
};
const transformMargin = createTransformBox('margin');

const transformPadding = createTransformBox('padding');

const transitionProperty = 'transitionProperty';
const transitionDuration = 'transitionDuration';
const transitionTimingFunction = 'transitionTimingFunction';
const transitionDelay = 'transitionDelay';
const transformTransition = (decl) => {
    const CHUNK_REGEXP = /^(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?\s*(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?$/;
    const { value, important, raws, source } = decl;
    const result = [];
    const match = value.match(CHUNK_REGEXP);
    if (!match) {
        return result;
    }
    match[1] &&
        result.push(createDecl(transitionProperty, match[1], important, raws, source));
    match[2] &&
        result.push(createDecl(transitionDuration, match[2], important, raws, source));
    match[3] &&
        result.push(createDecl(transitionTimingFunction, match[3], important, raws, source));
    match[4] &&
        result.push(createDecl(transitionDelay, match[4], important, raws, source));
    return result;
};

const DeclTransforms = {
    transition: transformTransition,
    margin: transformMargin,
    padding: transformPadding,
    border: transformBorder,
    background: transformBackground,
};
{
    extend(DeclTransforms, {
        borderTop: transformBorder,
        borderRight: transformBorder,
        borderBottom: transformBorder,
        borderLeft: transformBorder,
        borderStyle: transformBorderStyle,
        borderWidth: transformBorderWidth,
        borderColor: transformBorderColor,
        borderRadius: transformBorderRadius,
        flexFlow: transformFlexFlow,
    });
}
const expanded = Symbol('expanded');
const expand = {
    postcssPlugin: 'nvue:expand',
    Declaration(decl) {
        if (decl[expanded]) {
            return;
        }
        const transform = DeclTransforms[decl.prop];
        if (transform) {
            const res = transform(decl);
            const isSame = res.length === 1 && res[0] === decl;
            if (!isSame) {
                decl.replaceWith(res);
            }
        }
        decl[expanded] = true;
    },
};

export { expand };
