<template>
    <div class="bmiCalculatorGraph">
        <div class="bmiCalculatorGraph__fields">
            <div class="bmiCalculatorGraph__fields__row">
                <div class="bmiCalculatorGraph__fields__column bmiCalculatorGraph__fields__column--left">
                    <fieldset
                        class="bmiCalculatorGraph__fields__fieldset"
                        :class="[showHeightError ? 'bmiCalculatorGraph__fields__fieldset--error' : '']"
                        v-show="heightIsMetric"
                    >
                        <legend>Height (cm)</legend>

                        <div class="row">
                            <div class="inputWrap">
                                <label
                                    :for="'question_' + staticInput.id + '_bmi_height_cm'"
                                    class="bmiCalculatorGraph__fields__label"
                                    :class="[heightCm ? 'bmiCalculatorGraph__fields__label--show' : '']"
                                    >cm</label
                                >

                                <input
                                    :id="'question_' + staticInput.id + '_bmi_height_cm'"
                                    type="text"
                                    :name="staticInput.name + '[bmi][metric][height_cm]'"
                                    v-model="heightCm"
                                    @wheel="preventScroll"
                                    placeholder="cm"
                                    @input="calculationHandler"
                                    @focus="showHeightError = false"
                                />
                            </div>
                            <!-- .inputWrap -->
                        </div>
                        <!-- .row -->
                    </fieldset>

                    <fieldset
                        class="bmiCalculatorGraph__fields__fieldset"
                        :class="[showHeightError ? 'bmiCalculatorGraph__fields__fieldset--error' : '']"
                        v-show="!heightIsMetric"
                    >
                        <legend>Height (feet)</legend>

                        <div class="row">
                            <div class="inputWrap">
                                <label
                                    :for="'question_' + staticInput.id + '_bmi_height_feet'"
                                    class="bmiCalculatorGraph__fields__label"
                                    :class="[heightFeet ? 'bmiCalculatorGraph__fields__label--show' : '']"
                                    >ft</label
                                >

                                <input
                                    :id="'question_' + staticInput.id + '_bmi_height_feet'"
                                    type="text"
                                    :name="staticInput.name + '[bmi][imperial][height_feet]'"
                                    v-model="heightFeet"
                                    @wheel="preventScroll"
                                    placeholder="ft"
                                    @input="calculationHandler"
                                    @focus="showHeightError = false"
                                />
                            </div>
                            <!-- .inputWrap -->

                            <div class="inputWrap">
                                <label
                                    :for="'question_' + staticInput.id + '_bmi_height_inches'"
                                    class="bmiCalculatorGraph__fields__label"
                                    :class="[heightInches ? 'bmiCalculatorGraph__fields__label--show' : '']"
                                    >in</label
                                >

                                <input
                                    :id="'question_' + staticInput.id + '_bmi_height_inches'"
                                    type="text"
                                    :name="staticInput.name + '[bmi][imperial][height_inches]'"
                                    v-model="heightInches"
                                    @wheel="preventScroll"
                                    placeholder="in"
                                    @input="calculationHandler"
                                    @focus="showHeightError = false"
                                />
                            </div>
                            <!-- .inputWrap -->
                        </div>
                        <!-- .row -->
                    </fieldset>

                    <a
                        href="#toggle-height"
                        class="bmiCalculatorGraph__fields__toggle"
                        @click.prevent="toggleHeightType"
                    >
                        <span v-if="heightIsMetric">Change to feet (ft)</span>
                        <span v-else>Change to centimeters (cm)</span>
                    </a>
                </div>
                <!-- .bmiCalculatorGraph__fields__column -->

                <div class="bmiCalculatorGraph__fields__column bmiCalculatorGraph__fields__column--right">
                    <fieldset
                        class="bmiCalculatorGraph__fields__fieldset"
                        :class="[showWeightError ? 'bmiCalculatorGraph__fields__fieldset--error' : '']"
                        v-show="weightIsMetric"
                    >
                        <legend>Weight (kg)</legend>

                        <div class="row">
                            <div class="inputWrap">
                                <label
                                    :for="'question_' + staticInput.id + '_bmi_height_kg'"
                                    class="bmiCalculatorGraph__fields__label"
                                    :class="[weightKg ? 'bmiCalculatorGraph__fields__label--show' : '']"
                                    >kg</label
                                >

                                <input
                                    :id="'question_' + staticInput.id + '_bmi_height_kg'"
                                    type="text"
                                    :name="staticInput.name + '[bmi][metric][weight_kg]'"
                                    v-model="weightKg"
                                    @wheel="preventScroll"
                                    placeholder="kg"
                                    @input="calculationHandler"
                                    @focus="showWeightError = false"
                                />
                            </div>
                            <!-- .inputWrap -->
                        </div>
                        <!-- .row -->
                    </fieldset>

                    <fieldset
                        class="bmiCalculatorGraph__fields__fieldset"
                        :class="[showWeightError ? 'bmiCalculatorGraph__fields__fieldset--error' : '']"
                        v-show="!weightIsMetric"
                    >
                        <legend>Weight (lbs)</legend>

                        <div class="row">
                            <div class="inputWrap">
                                <label
                                    :for="'question_' + staticInput.id + '_bmi_height_stone'"
                                    class="bmiCalculatorGraph__fields__label"
                                    :class="[weightStone ? 'bmiCalculatorGraph__fields__label--show' : '']"
                                    >st</label
                                >

                                <input
                                    :id="'question_' + staticInput.id + '_bmi_height_stone'"
                                    type="text"
                                    :name="staticInput.name + '[bmi][imperial][weight_stone]'"
                                    v-model="weightStone"
                                    @wheel="preventScroll"
                                    placeholder="st"
                                    @input="calculationHandler"
                                    @focus="showWeightError = false"
                                />
                            </div>
                            <!-- .inputWrap -->

                            <div class="inputWrap">
                                <label
                                    :for="'question_' + staticInput.id + '_bmi_height_pounds'"
                                    class="bmiCalculatorGraph__fields__label"
                                    :class="[weightPounds ? 'bmiCalculatorGraph__fields__label--show' : '']"
                                    >lbs</label
                                >

                                <input
                                    :id="'question_' + staticInput.id + '_bmi_height_pounds'"
                                    type="text"
                                    :name="staticInput.name + '[bmi][imperial][weight_pounds]'"
                                    v-model="weightPounds"
                                    @wheel="preventScroll"
                                    placeholder="lbs"
                                    @input="calculationHandler"
                                    @focus="showWeightError = false"
                                />
                            </div>
                            <!-- .inputWrap -->
                        </div>
                        <!-- .row -->
                    </fieldset>

                    <a
                        href="#toggle-weight"
                        class="bmiCalculatorGraph__fields__toggle"
                        @click.prevent="toggleWeightType"
                    >
                        <span v-if="heightIsMetric">Change to pounds (lbs)</span>
                        <span v-else>Change to kilograms (kg)</span>
                    </a>
                </div>
                <!-- .bmiCalculatorGraph__fields__column -->
            </div>
            <!-- .bmiCalculatorGraph__fields__row -->

            <div class="bmiCalculatorGraph__actions">
                <button @click.prevent="calculateButtonHandler" class="button" v-html="calculatorButtonText"></button>
            </div>
        </div>
        <!-- .bmiCalculatorGraph__fields -->

        <div class="bmiCalculatorGraph__calculation">
            <div class="bmiCalculatorGraph__indication">
                <div class="bmiCalculatorGraph__indication__text" v-if="bmiCategoryName.length && bmiCalculation > 0">
                    Your BMI of
                    <strong class="bmiCalculatorGraph__indication__value" v-html="bmiCalculation"></strong>
                    indicates that you currently are:
                    <strong class="bmiCalculatorGraph__indication__category" v-html="bmiCategoryName"></strong>
                </div>

                <div class="bmiCalculatorGraph__indication__text" v-else>BMI Indicator</div>

                <div class="bmiCalculatorGraph__indication__chart">
                    <div class="bmiCalculatorGraph__indication__chart__bar">
                        <span
                            class="bmiCalculatorGraph__indication__chart__marker"
                            :style="'left:' + bmiBarMarkerPosition + '%;'"
                        ></span>
                    </div>
                </div>
            </div>

            <input type="hidden" v-model="currentBMIValue" :name="staticInput.name + '[bmi][value]'" />
        </div>
        <!-- .bmiCalculatorGraph__calculation -->
    </div>
    <!-- .bmiCalculatorGraph -->
</template>

<script>
export default {
    name: 'bmiCalculatorGraph',

    props: {
        bmiCategories: {
            type: [Array, Object],

            default: () => {
                return [
                    {
                        name: 'Underweight',
                        min: 0,
                        max: 18.5,
                        section: 1,
                        content: '',
                    },
                    {
                        name: 'Normal Weight',
                        min: 18.5,
                        max: 25,
                        section: 2,
                        content: '',
                    },
                    {
                        name: 'Overweight',
                        min: 25,
                        max: 30,
                        section: 3,
                        content: '',
                    },
                    {
                        name: 'Obese',
                        min: 30,
                        max: 40,
                        section: 4,
                        content: '',
                    },
                ]
            },
        },

        assessmentLink: {
            type: [Array, Object],
            default() {
                return {
                    text: 'Start your free assessment',
                    url: '#assessment-links',
                }
            },
        },
    },

    data() {
        return {
            enabled: false, //Enable on button click
            allowEvent: false,
            calculatorButtonText: 'Calculate',
            staticInput: {
                id: 0,
                name: 'bmi_block_input',
            },
            type: 'Metric', //'Metric' || 'Imperial' (Potentially remove this)
            heightCm: '',
            weightKg: '',
            heightFeet: '',
            heightInches: '',
            weightStone: '',
            weightPounds: '',
            currentBMIValue: 0,
            changed: {
                heightCm: false,
                weightKg: false,
                heightFeet: false,
                weightStone: false,
                heightInches: false,
                weightPounds: false,
            },
            limits: {
                height: {
                    max: 220,
                    min: 100,
                },
                weight: {
                    max: 180,
                    min: 40,
                },
            },

            heightType: 'metric', // 'metric' || 'imperial'
            weightType: 'metric', // 'metric' || 'imperial'

            categorySection: 1,

            showHeightError: false,
            showWeightError: false,
        }
    },

    created() {
        this.staticInput.id = this.getCurrentTimestamp()
    },

    watch: {
        bmiCalculation(newVal, oldVal) {
            if (newVal !== oldVal && newVal > 0) {
                this.calculationHandler()
            }
        },
    },

    computed: {
        activeCategory() {
            const bmi = this.bmiCalculation

            let category = this.bmiCategories.find((cat) => {
                if (bmi >= cat.min && bmi < cat.max) {
                    this.categorySection = cat.section
                    return cat
                }

                return false
            })

            const categorySet = category ?? false

            if (!categorySet) {
                if (bmi >= 30) {
                    this.categorySection = 4
                } else {
                    this.categorySection = 1
                }
            }

            return categorySet
        },

        bmiCategoryName() {
            const category = this.bmiCategories.find((category) => {
                return this.categorySection === category.section
            })

            return category?.name?.length ? category.name : ''
        },

        bmiBarMarkerPosition() {
            const bmi = Number(this.bmiCalculation ?? 0)

            const category = this.activeCategory

            if (category) {
                let totalPercentage = (category.section - 1) * 25

                let min = category.min,
                    max = category.max

                let offsetMax = max - min,
                    offsetBmi = bmi - min

                let percentage = (offsetBmi / offsetMax) * 100
                let additionalPercentage = (percentage / 100) * 25

                totalPercentage += additionalPercentage
                totalPercentage = totalPercentage > 98 ? 98 : totalPercentage
                totalPercentage = totalPercentage < 2 ? 2 : totalPercentage

                return totalPercentage
            } else {
                if (bmi > 30) {
                    return 98
                }
            }

            return 0
        },

        heightIsMetric() {
            return this.heightType === 'metric'
        },

        weightIsMetric() {
            return this.weightType === 'metric'
        },

        metricHeight() {
            let height = 0

            if (this.heightIsMetric) {
                height = parseInt(this.heightCm)
            } else {
                if (parseInt(this.heightFeet) > 0) {
                    height = this.heightImperialToMetric()
                }
            }

            return Number(height ?? 0)
        },

        metricWeight() {
            let weight = 0

            if (this.weightIsMetric) {
                weight = parseInt(this.weightKg)
            } else {
                if (parseInt(this.weightStone) > 0) {
                    weight = this.weightImperialToMetric()
                }
            }

            return Number(weight ?? 0)
        },

        bmiCalculation() {
            if (!this.enabled) {
                return 0
            }

            let height = 0,
                weight = 0

            if (this.heightIsMetric) {
                height = parseInt(this.heightCm)
            } else {
                if (parseInt(this.heightFeet) > 0) {
                    height = this.heightImperialToMetric()
                }
            }

            if (this.weightIsMetric) {
                weight = parseInt(this.weightKg)
            } else {
                if (parseInt(this.weightStone) > 0) {
                    weight = this.weightImperialToMetric()
                }
            }

            return this.calculateMetricBmi(height, weight)
        },

        heightIsValid() {
            let height = 0
            let maxHeight = this.limits.height.max
            let minHeight = this.limits.height.min

            if (this.heightIsMetric) {
                height = parseInt(this.heightCm)
            } else {
                height = this.heightImperialToMetric()
                maxHeight -= 10
                minHeight += 21
            }

            if (height >= minHeight && height <= maxHeight) {
                return true
            }

            return false
        },

        weightIsValid() {
            let weight = 0
            let maxWeight = this.limits.weight.max
            let minWeight = this.limits.weight.min

            if (this.weightIsMetric) {
                weight = parseInt(this.weightKg)
            } else {
                weight = this.weightImperialToMetric()
                maxWeight += 10
                minWeight -= 2
            }

            if (weight >= minWeight && weight <= maxWeight) {
                return true
            }

            return false
        },
    },

    methods: {
        calculateMetricBmi(height, weight) {
            if (weight > 0 && height > 0) {
                return (weight / Math.pow(height / 100, 2)).toFixed(2)
            } else {
                return 0
            }
        },

        //Toggle between metric and imperial for height
        toggleHeightType() {
            this.showHeightError = false
            return (this.heightType = this.heightType === 'metric' ? 'imperial' : 'metric')
        },

        toggleWeightType() {
            this.showWeightError = false
            return (this.weightType = this.weightType === 'metric' ? 'imperial' : 'metric')
        },

        getCurrentTimestamp() {
            return Date.now()
        },

        //Convert imperial to metric
        heightImperialToMetric() {
            let height = 0

            if (parseInt(this.heightFeet) > 0) {
                let totalInches = parseInt(this.heightFeet) * 12 + parseInt(this.heightInches)
                if (!isNaN(totalInches)) {
                    height = Math.floor(totalInches * 2.54)
                }
            }

            return height
        },

        weightImperialToMetric() {
            let weight = 0

            if (parseInt(this.weightStone) > 0) {
                let totalPounds = Math.floor(parseInt(this.weightStone) * 14) + parseInt(this.weightPounds)

                if (!isNaN(totalPounds)) {
                    weight = Math.floor(totalPounds * 0.453592)
                }
            }

            return weight
        },

        //Stop input spinner randomly selecting a value
        preventScroll(event) {
            if (event.target === document.activeElement) {
                event.preventDefault()
            }
        },

        clearfields() {
            this.currentBMIValue = 0

            if (this.heightIsMetric) {
                this.heightCm = ''
                this.changed.heightCm = false
            } else {
                this.heightFeet = ''
                this.heightInches = ''
                this.changed.heightFeet = false
                this.changed.heightInches = false
            }

            if (this.weightIsMetric) {
                this.weightKg = ''
                this.changed.weightKg = false
            } else {
                this.weightStone = ''
                this.weightPounds = ''
                this.changed.weightStone = false
                this.changed.weightPounds = false
            }
        },

        updateAllowed() {
            let height = Number(this.heightCm ?? 0)
            let weight = Number(this.weightKg ?? 0)

            if (!this.heightIsMetric) {
                height = this.heightImperialToMetric()
            }

            if (!this.weightIsMetric) {
                weight = this.weightImperialToMetric()
            }

            return height > 0 && weight > 0
        },

        calculateButtonHandler() {
            if (this.heightIsValid && this.weightIsValid) {
                this.calculationHandler(true, true)
            } else {
                if (!this.heightIsValid) {
                    this.showHeightError = true
                }

                if (!this.weightIsValid) {
                    this.showWeightError = true
                }
            }
        },

        calculationHandler(setAllowEvent = false, scrollIntoView = false) {
            if (this.updateAllowed()) {
                if (setAllowEvent === true) {
                    this.allowEvent = true
                }

                this.enabled = true
                this.currentBMIValue = this.bmiCalculation

                if (this.allowEvent) {
                    this.calculatorButtonText = 'Update'

                    EventBus.$emit('weightLossData', {
                        projection: this.projectWeightLoss(this.metricWeight, this.metricHeight, 72),
                        isMetric: this.weightIsMetric,
                        scrollIntoView: scrollIntoView,
                        bmiCategories: this.bmiCategories,
                        bmi: this.bmiCalculation,
                        assessmentLink: this.assessmentLink,
                    })
                }
            }
        },

        //Mounjaro weight loss projection
        projectWeightLoss(startWeight, height, weeks) {
            // Default parameters (calibrated to Mounjaro trial data)

            const P_max = 0.21 // 21% plateau
            const k = 0.093 // logistic growth rate
            const t_mid = 9 // inflection point (weeks)

            // Normalization for week 0 (ensures 0% loss at start)
            const f0 = 1 / (1 + Math.exp(k * t_mid))

            let data = []
            for (let week = 0; week <= weeks; week++) {
                // Logistic fraction
                let frac = 1 / (1 + Math.exp(-k * (week - t_mid)))
                // Normalize curve so it starts at 0% and plateaus at P_max
                let percentLost = P_max * ((frac - f0) / (1 - f0))
                if (percentLost < 0) percentLost = 0 // safety clamp
                // Compute weight
                let weightNow = startWeight * (1 - percentLost)

                data.push({
                    week: week,
                    weight: Number(weightNow.toFixed(1)),
                    percentage: Number((percentLost * 100).toFixed(1)),
                    bmi: this.calculateMetricBmi(height, weightNow),
                })
            }

            return data
        },
    },
}
</script>

<style lang="less">
.bmiCalculatorGraph {
    width: 100%;
    position: relative;

    &__fields {
        padding: 0;
        margin: 0;

        &__label {
            opacity: 0;
            visibility: hidden;
            pointer-events: none;
            transition:
                opacity 200ms linear,
                visibility 200ms linear;

            &--show {
                opacity: 1;
                visibility: visible;
                pointer-events: auto;
                transition:
                    opacity 200ms linear,
                    visibility 200ms linear;
            }
        }

        .row {
            display: flex;
            flex-direction: row;
            flex-wrap: wrap;
            gap: 15px;
            margin: 0 0 8px;

            @media @mobile {
                flex-wrap: wrap;
            }

            &:before,
            &:after {
                display: none;
            }

            .inputWrap {
                position: relative;
                float: none;
                clear: both;
                padding: 0;
                flex: 1 0 200px;

                @media @mobile {
                    flex: 0 0 100%;
                }

                input:not([type='checkbox']),
                input:not([type='radio']) {
                    position: relative;
                    z-index: 1;
                    float: none !important;
                    clear: both !important;
                    -moz-appearance: textfield;
                    padding-right: 40px;

                    /* Chrome, Safari, Edge, Opera */
                    &::-webkit-outer-spin-button,
                    &::-webkit-inner-spin-button {
                        -webkit-appearance: none;
                        margin: 0;
                    }
                }

                label {
                    background: #ffffff;
                    color: var(--text_color);
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    padding: 4px;
                    width: 40px;
                    height: ~'calc(100% - 4px)';
                    position: absolute;
                    top: 2px;
                    right: 2px;
                    bottom: 2px;
                    z-index: 2;
                    .rems(16);
                    line-height: normal;
                    margin: 0;
                    border-radius: 0 90px 90px 0;
                    font-weight: 300;
                }
            }
        }

        &__fieldset {
            legend {
                display: block;
                .rems(15);
                font-weight: 400;
                line-height: 1.2em;
                color: var(--text_field_color);
                margin: 0 0 8px;
            }

            &--error {
                --text_field_color: var(--alert_color);
                --form_border_color: var(--alert_color);
            }
        }

        &__row {
            display: flex;
            gap: 20px;
            width: 100%;
            padding: 0 0 20px;

            @media @mobile {
                flex-wrap: wrap;
            }
        }

        &__column {
            flex: 1 1 50%;

            @media @mobile {
                flex: 0 0 100%;
            }
        }

        &__toggle {
            .rems(11);
            font-weight: 400;
            line-height: 1.2em;
            text-decoration: underline;

            &:hover,
            &:active,
            &:focus-visible {
                text-decoration: none;
            }
        }
    }

    &__actions {
        .button {
            width: 100%;
        }
    }

    &__calculation {
        padding: 0;
        margin: 20px 0;

        span {
            font-weight: inherit;
        }
    }

    &__indication {
        &__text {
            .rems(15);
            font-weight: 400;
            line-height: 1.2em;
            margin: 0 0 9px;
        }

        &__chart {
            --indicator_color: #a6cbce;

            position: relative;
            width: 100%;
            padding: 6px 0;

            &__bar {
                position: relative;
                display: block;
                width: 100%;
                height: 20px;
                border-radius: 20px;
                border: 1px solid var(--indicator_color);
                background: linear-gradient(
                    90deg,
                    rgba(255, 255, 255, 1) 0%,
                    rgba(255, 255, 255, 1) 25%,
                    rgba(166, 203, 206, 1) calc(25% + 3px),
                    rgba(242, 250, 250, 1) 25%,
                    rgba(242, 250, 250, 1) 50%,
                    rgba(166, 203, 206, 1) calc(50% + 3px),
                    rgba(219, 240, 242, 1) 50%,
                    rgba(219, 240, 242, 1) 75%,
                    rgba(166, 203, 206, 1) calc(75% + 3px),
                    rgba(191, 229, 232, 1) 75%,
                    rgba(191, 229, 232, 1) 100%
                );
            }

            &__marker {
                display: block;
                border: 1px solid var(--indicator_color);
                background: #fff;
                width: 32px;
                height: 32px;
                position: absolute;
                top: 50%;
                left: 0;
                transform: translate(-50%, -50%);
                border-radius: 50%;
                transition: left 1000ms ease-in-out;
            }
        }
    }
}
</style>
