const preact = require('preact');
const get = require('lodash/get');

const shapes = require('./components/shapes');

const stringSort = (k, desc) => {
    if (desc) {
        return (a, b) => {
            if (!get(a, k)) return 1;
            if (!get(b, k)) return -1;
            return get(b, k).localeCompare(get(a, k));
        };
    }
    return (a, b) => {
        if (!get(a, k)) return 1;
        if (!get(b, k)) return -1;
        return get(a, k).localeCompare(get(b, k));
    };
};

const numSort = (k, desc) => {
    if (desc) {
        return (a, b) => {
            if (!get(a, k)) return 1;
            if (!get(b, k)) return -1;
            return get(b, k) - get(a, k);
        };
    }
    return (a, b) => {
        if (!get(a, k)) return 1;
        if (!get(b, k)) return -1;
        return get(a, k) - get(b, k);
    };
};

const genAvatar = name => {
    let hash = 0;
    if (name.length === 0) return hash;
    // Probs don't need to hash using the whole string
    for (let i = 0; i < name.length; i += 1) {
        const chr = name.charCodeAt(i);
        hash = ((hash << 5) - hash) + chr;
        hash = hash % 10000;
    }
    return `${hash}`;
};

function requestAvatar(name) {
    const id = genAvatar(name);
    const req = new Request(`/assets/molecules/${id}.svg`);
    return fetch(req)
        .then(res => res.text())
        .then(svg => svg);
}

function crypAvatar(name) {
    return (
        <img
            src={`/molecules/${genAvatar(name)}.svg`}
            height="500"
            onError={event => event.target.setAttribute('src', '/molecules/726.svg')}
        />
    );
}

const NULL_UUID = '00000000-0000-0000-0000-000000000000';

const STATS = {
    redDamage: { stat: 'red_damage', colour: 'red', svg: shapes.circle },
    greenDamage: { stat: 'green_damage', colour: 'green', svg: shapes.circle },
    blueDamage: { stat: 'blue_damage', colour: 'blue', svg: shapes.circle },
    speed: { stat: 'speed', colour: 'white', svg: shapes.triangle },
    redLife: { stat: 'red_life', colour: 'red', svg: shapes.square },
    greenLife: { stat: 'green_life', colour: 'green', svg: shapes.square },
    blueLife: { stat: 'blue_life', colour: 'blue', svg: shapes.square },
};

const SPECS = {
    Life: { colour: 'white', caption: 'Life', svg: shapes.square },
    GreenLifeI: { colour: 'green', caption: 'Life', svg: shapes.square },
    RedLifeI: { colour: 'red', caption: 'Life', svg: shapes.square },
    BlueLifeI: { colour: 'blue', caption: 'Life', svg: shapes.square },
    GRLI: { colour: 'yellow', caption: 'Life', svg: shapes.square },
    GBLI: { colour: 'cyan', caption: 'Life', svg: shapes.square },
    RBLI: { colour: 'purple', caption: 'Life', svg: shapes.square },

    Damage: { colour: 'white', caption: 'Damage', svg: shapes.circle },
    RedDamageI: { colour: 'red', caption: 'DamageI', svg: shapes.circle },
    BlueDamageI: { colour: 'blue', caption: 'DamageI', svg: shapes.circle },
    GreenDamageI: { colour: 'green', caption: 'DamageI', svg: shapes.circle },
    GRDI: { colour: 'yellow', caption: 'DamageI', svg: shapes.circle },
    GBDI: { colour: 'cyan', caption: 'DamageI', svg: shapes.circle },
    RBDI: { colour: 'purple', caption: 'DamageI', svg: shapes.circle },

    Speed: { colour: 'white', caption: 'Speed', svg: shapes.triangle },
    RedSpeed: { colour: 'red', caption: 'Speed', svg: shapes.triangle },
    BlueSpeedI: { colour: 'blue', caption: 'Speed', svg: shapes.triangle },
    GreenSpeedI: { colour: 'green', caption: 'Speed', svg: shapes.triangle },
    GRSpeedI: { colour: 'yellow', caption: 'Speed', svg: shapes.triangle },
    GBSpeedI: { colour: 'cyan', caption: 'Speed', svg: shapes.triangle },
    RBSpeedI: { colour: 'purple', caption: 'Speed', svg: shapes.triangle },

};

const COLOUR_ICONS = {
    red: { colour: 'red', caption: 'red', svg: shapes.square },
    blue: { colour: 'blue', caption: 'blue', svg: shapes.square },
    green: { colour: 'green', caption: 'green', svg: shapes.square },
};

function eventClasses(resolution, cryp) {
    if (!resolution) return '';
    const startSkill = resolution.stage === 'START_SKILL';
    const endSkill = resolution.stage === 'END_SKILL';
    const postSkill = resolution.stage === 'POST_SKILL';
    const source = cryp.id === resolution.source.id;
    const target = cryp.id === resolution.target.id;

    // not involved at all. blur them
    if (!(source || target)) return 'unfocus';

    // not the target. just ignore for now
    // if (cryp.id !== resolution.target.id) return '';

    const [type, event] = resolution.event;

    if (type === 'Ko') {
        if (target) return 'ko';
    }

    if (type === 'Disable') {
        const { skill, disable } = event;
    }

    if (type === 'Immunity') {
        const { skill, immunity } = event;
    }

    if (type === 'TargetKo') {
        const { skill } = event;
    }

    if (type === 'Damage') {
        const { skill, amount, mitigation, colour } = event;
        // Highlight the flow of damage from source -> target
        if (source && startSkill) return 'active-skill';
        if (target && endSkill) return 'active-skill';

        // Deal damage to cryp and return effect
        if (target && postSkill) {
            cryp.green_life.value -= amount;
            if (colour === 'RedDamage') {
                cryp.red_life.value -= mitigation;
                return 'red-damage';
            }
            if (colour === 'BlueDamage') {
                cryp.blue_life.value -= mitigation;
                return 'blue-damage';
            }
            if (colour === 'GreenDamage') return 'green-damage';
        }

    }

    if (type === 'Healing') {
        const { skill, amount, overhealing } = event;
        if (source && startSkill) return 'active-skill';
        if (target && endSkill) return 'active-skill';
        if (target && postSkill) {
            cryp.green_life.value += amount;
            return 'green-damage';
        }
    }

    if (type === 'Inversion') {
        const { skill } = event;
    }

    if (type === 'Reflection') {
        const { skill } = event;
    }

    if (type === 'Effect') {
        const { skill, effect, duration } = event;
        if (source && startSkill) return 'active-skill';
        if (target && endSkill) return 'active-skill';
    }

    if (type === 'Skill') {
        const { skill } = event;
        // Highlight the flow of damage from source -> target
        if (source && startSkill) return 'active-skill';
        if (target && endSkill) return 'active-skill';
    }

    if (type === 'Removal') {
        const { effect } = event;
    }

    if (type === 'Recharge') {
        const { skill, red, blue } = event;
        if (source && startSkill) return 'active-skill';
        if (target && endSkill) return 'active-skill';
        if (target && postSkill) {
            cryp.red_life.value += red;
            cryp.blue_life.value += blue;
            if (red > 0 && blue > 0) return 'purple-damage';
            if (red > 0) return 'red-damage';
            if (blue > 0) return 'blue-damage';
        }
    }

    if (type === 'Evasion') {
        const { skill, evasion_rating } = event;
    }

    return '';
}

function getCombatSequence(event) {
    if (!event) return false;
    // Skip combat animations depending on event type, expandable in future
    const dotTicks = ['DecayTick', 'CorruptionTick', 'TriageTick', 'SiphonTick', 'StrangleTick'];

    if (['Skill', 'AoeSkill'].includes(event[0])) return ['START_SKILL', 'END_SKILL'];
    if (['Immunity'].includes(event[0])) return ['START_SKILL', 'POST_SKILL'];
    if (['Removal'].includes(event[0])) return ['POST_SKILL'];

    if (['Healing'].includes(event[0])
        && (event[1].skill === 'Slay'
            || event[1].skill === 'SiphonTick'
            || event[1].skill === 'Purify'
            || event[1].skill === 'Sleep')) return ['POST_SKILL'];

    if (['Recharge'].includes(event[0])
        && (event[1].skill === 'Reflect')) return ['POST_SKILL'];


    if (event[0] === 'Effect'
        && (['Ruin', 'Taunt', 'Strangling', 'Parry'].includes(event[1].skill)
        || (event[1].skill === 'Decay' && event[1].effect === 'Wither'))) return ['POST_SKILL'];

    if (['Damage'].includes(event[0])
        && ((event[1].skill === 'Chaos' && event[1].colour === 'RedDamage')
            || event[1].skill === 'Silence'
            || event[1].skill === 'Snare')) return ['POST_SKILL'];

    if (['Ko'].includes(event[0])
        || (event[1].skill === 'Throw' && event[1].effect === 'Vulnerable')) return ['POST_SKILL'];

    if (dotTicks.includes(event[1].skill)) return ['END_SKILL', 'POST_SKILL'];
    return ['START_SKILL', 'END_SKILL', 'POST_SKILL'];
}

function getCombatText(cryp, resolution) {
    if (!resolution) return '';


    const [type, event] = resolution.event;
    const source = cryp.id === resolution.source.id;
    const target = cryp.id === resolution.target.id;
    const startSkill = resolution.stage === 'START_SKILL';
    const endSkill = resolution.stage === 'END_SKILL';
    const postSkill = resolution.stage === 'POST_SKILL';

    if (type === 'Ko') {
        if (postSkill && target) return 'KO!';
    }

    if (type === 'Disable') {
        const { skill, disable } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return `${disable}`;
    }

    if (type === 'Immunity') {
        const { skill, immunity } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return 'IMMUNE';
    }

    if (type === 'TargetKo') {
        const { skill } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
    }

    if (type === 'Damage') {
        const { skill, amount, mitigation, colour } = event;
        const mitigationText = mitigation
            ? `(${mitigation})`
            : '';
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return `${amount} ${mitigationText}`;
    }

    if (type === 'Healing') {
        const { skill, amount, overhealing } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return `${amount} (${overhealing} OH)`;
    }

    if (type === 'Inversion') {
        const { skill } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return 'INVERT';
    }

    if (type === 'Reflection') {
        const { skill } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return 'REFLECT';
    }

    if (type === 'Effect') {
        const { skill, effect, duration } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return `+ ${effect} ${duration}T`;
    }

    if (type === 'Recharge') {
        const { skill, red, blue } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
        if (postSkill && target) return `+${red}R ${blue}B`;
    }

    if (type === 'Skill' || type === 'AoeSkill') {
        const { skill } = event;
        if (startSkill && source) return `${skill}`;
        if (endSkill && target) return `${skill}`;
    }

    if (type === 'Removal') {
        const { effect } = event;
        if (postSkill && target) return `-${effect}`;
    }


    return '';
}

function convertVar(v) {
    if (['Red', 'Green', 'Blue'].includes(v)) {
        return (
            shapes.vboxColour(v.toLowerCase())
        );
    }
    return v || <span>&nbsp;</span>;
    // uncomment for double borders in vbox;
    // if (v) {
    //     return <div>{v}</div>;
    // }
    // return;
}

module.exports = {
    stringSort,
    convertVar,
    numSort,
    genAvatar,
    crypAvatar,
    requestAvatar,
    eventClasses,
    getCombatSequence,
    getCombatText,
    NULL_UUID,
    STATS,
    SPECS,
    COLOUR_ICONS,
};
