<template>
    <div
        class="hy-input"
        :style="{ maxWidth: extend ? '' : 'max-content' }"
        :class="{
            hideText: loading,
            iconLeft: $slots.left || $slots.default,
            iconRight: $slots.right,
            invalidTextVisible: invalid && invalidText,
            transitionsEnabled,
        }"
    >
        <input
            :placeholder="placeholder"
            :type="type"
            :disabled="disabled || loading"
            :value="modelValue"
            @input="$emit('update:modelValue', $event.target.value)"
            :class="[variantParsed, sizeParsed, { hideText: loading, invalid, isGhost, isGhostBg, isSolid }]"
            v-bind="attributes"
        />

        <div class="icons" v-if="$slots.left || $slots.default || $slots.right">
            <span class="iconSlot left" :class="[sizeParsed, { muted: disabled || loading }]" v-if="$slots.left || $slots.default">
                <slot name="left" />
                <slot />
            </span>

            <span class="iconSlot right" :class="[sizeParsed, { muted: disabled || loading }]" v-if="$slots.right">
                <slot name="right" />
            </span>
        </div>

        <p class="invalidText" ref="invalidText" v-show="invalid && invalidText" :class="{ muted: disabled || loading }">
            {{ invalidText }}
        </p>

        <transition name="hy-fade-transition">
            <HyLoader v-if="loading && progress == null" :width="25" :height="25" :line="3" />
        </transition>
        <transition name="hy-fade-transition">
            <HyProgressBar
                v-if="loading && isNaN(progress) == false && typeof progress == 'number'"
                :progress="progress"
                :width="progressWidth"
            />
        </transition>
    </div>
</template>

<script>
import HyLoader from "./Loader.vue";
import HyProgressBar from "./ProgressBar.vue";

import { getBorderRadius } from "../utils/getBorderRadius.js";

export default {
    name: "HyInput",
    props: {
        modelValue: null,
        placeholder: {
            type: String,
            default: "",
        },
        variant: {
            type: String,
            default: null,
        },
        size: {
            type: String,
            default: null,
        },
        type: {
            type: String,
            default: "text",
        },
        required: {
            type: Boolean,
            default: false,
        },
        loading: {
            type: Boolean,
            default: false,
        },
        progress: {
            type: Number,
            default: null,
        },
        progressWidth: {
            type: String,
            default: "min(calc(100% - 2 * var(--hy-element-padding)), 200px)",
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        invalid: {
            type: Boolean,
            default: false,
        },
        invalidText: {
            type: String,
            default: null,
        },
        extend: {
            type: Boolean,
            default: true,
        },
        doAutofocus: {
            type: Boolean,
            default: false,
        },
    },
    data() {
        return {
            borderRadius: "10",
            invalidTextHeight: "0px",
            invalidTextLeftClipPath: "",
            invalidTextRightClipPath: "",
            transitionsEnabled: false,
        };
    },
    computed: {
        attributes() {
            let attrs = { ...this.$attrs };

            // Delete attrs that shouldn't appear on the input element
            delete attrs.id;
            delete attrs.class;
            delete attrs.type;
            delete attrs.placeholder;
            delete attrs.loading;
            delete attrs.disabled;
            delete attrs.invalid;
            delete attrs.invalidText;
            delete attrs.extend;
            delete attrs.required;
            delete attrs.doAutofocus;

            return attrs;
        },

        sizeParsed() {
            let size = this.size ?? this.hy_defaults.size;
            let allowed = ["lg", "md", "sm"];

            if (allowed.includes(size)) return `size-${size}`;
            else return "size-lg";
        },

        variantParsed() {
            let allowed = [...this.hy_brand_theme.availableVariants, "secondary", "ghost", "ghost-bg"];
            allowed = allowed.filter((v) => v != "primary"); // Remove primary variant

            let variant = this.variant ?? this.hy_defaults.variant;

            if (allowed.includes(variant)) return variant;
            else return "secondary";
        },
        isGhostBg() {
            return this.variantParsed.startsWith("ghost-bg");
        },
        isGhost() {
            return this.variantParsed.startsWith("ghost") && this.variantParsed.startsWith("ghost-bg") == false;
        },
        isSolid() {
            return this.variantParsed.startsWith("ghost") == false && this.variantParsed.startsWith("ghost-bg") == false;
        },
        fontColor() {
            // Note: "ghost" and "ghost-bg" variants share the same variables => trim the "-bg" part
            return `var(--hy-variant-${this.variantParsed.replace("-bg", "")}-font-color)`;
        },
        backgroundColor() {
            return `var(--hy-variant-${this.variantParsed.replace("-bg", "")}-bg-color)`;
        },
        hoverColor() {
            return `var(--hy-variant-${this.variantParsed.replace("-bg", "")}-hover-color)`;
        },
        borderColor() {
            return `var(--hy-variant-${this.variantParsed.replace("-bg", "")}-border-color)`;
        },
        borderColorPressed() {
            if (this.isSolid == false) return "transparent";
            else return `var(--hy-variant-${this.variantParsed.replace("-bg", "")}-border-color-pressed)`;
        },
    },
    watch: {
        invalid: "grabElementValues",
        invalidText: "grabElementValues",
        loading: "grabElementValues",
        progress: "grabElementValues",
    },
    methods: {
        grabElementValues() {
            this.setBorderRadius();
            this.setInvalidTextHeight();
            this.setInvalidTextLeftClipPath();
            this.setInvalidTextRightClipPath();
        },

        setInvalidTextHeight() {
            if (!this.invalidText) this.invalidTextHeight = "0px";
            this.invalidTextHeight = `${this.$refs.invalidText.offsetHeight}px`;
        },
        setInvalidTextLeftClipPath() {
            this.invalidTextLeftClipPath = `path("M 0 0 a ${this.borderRadius} ${this.borderRadius} 0 0 0 ${this.borderRadius} ${this.borderRadius} L 0 ${this.borderRadius}")`;
        },
        setInvalidTextRightClipPath() {
            this.invalidTextRightClipPath = `path("M 0 ${this.borderRadius} a ${this.borderRadius} ${this.borderRadius} 0 0 0 ${this.borderRadius} -${this.borderRadius} L ${this.borderRadius} ${this.borderRadius}")`;
        },
        setBorderRadius() {
            this.borderRadius = getBorderRadius();
        },
    },
    mounted() {
        this.grabElementValues();

        if (this.doAutofocus) this.$el.querySelector("input").focus();

        requestAnimationFrame(() => (this.transitionsEnabled = true));
    },
    beforeUnmount() {
        if (this.doAutofocus) this.$el.querySelector("input").blur();
    },
    components: {
        HyLoader,
        HyProgressBar,
    },
};
</script>

<style lang="scss">
.hy-input {
    margin: var(--hy-element-margin);
    position: relative;
    min-width: max-content;

    &.transitionsEnabled {
        input,
        .iconSlot,
        .invalidText {
            transition: var(--hy-element-transition);
            transition-property: background-color, color, border, filter;
        }
    }

    input {
        -moz-appearance: none;
        -webkit-appearance: none;
        appearance: none;

        border-radius: var(--hy-element-border-radius);
        padding: var(--hy-element-padding);
        font-size: var(--hy-font-size);
        font-family: var(--hy-font-family);
        color: var(--hy-font-color-contrast);
        cursor: text;
        outline: none;
        width: 100%;

        &:disabled {
            cursor: not-allowed;
            filter: opacity(50%);
        }

        &.invalid {
            border: var(--hy-element-border-error) !important;
        }

        &.hideText {
            color: transparent;
        }

        // Sizes
        &.size-lg {
            padding: var(--hy-element-padding);
        }

        &.size-md {
            padding: var(--hy-element-padding-md);
        }

        &.size-sm {
            padding: var(--hy-element-padding-sm);
        }

        // Brand theme variants
        border: 2px solid transparent; // TODO: make border width a CSS variable

        &.isSolid {
            box-shadow: var(--hy-element-shadow);
            border-color: v-bind(borderColor); // Solids always have a border
            background-color: v-bind(backgroundColor); // Solids always have a background

            &:not([disabled]) {
                &:hover {
                    background-color: v-bind(hoverColor);
                }

                &:focus,
                &:active {
                    border-color: v-bind(borderColorPressed);
                }
            }
        }

        &.isGhost,
        &.isGhostBg {
            &:not([disabled]) {
                &:focus,
                &:active {
                    border-color: v-bind(borderColor);
                }
            }

            &.hideText {
                background-color: v-bind(backgroundColor); // Show background color if hideText is set (e.g. loading is active)
            }
        }

        &.isGhost {
            background-color: transparent;

            &:not([disabled]):hover {
                background-color: v-bind(backgroundColor);
            }
        }

        &.isGhostBg {
            background-color: v-bind(backgroundColor);

            &:not([disabled]):hover {
                background-color: v-bind(hoverColor);
            }
        }
    }

    &.iconLeft > input {
        padding-left: calc(var(--hy-element-padding) + 40px);
    }

    &.iconRight > input {
        padding-right: calc(var(--hy-element-padding) + 40px);
    }

    .icons {
        .iconSlot {
            pointer-events: none;
            position: absolute;
            margin: 0;
            padding: 0 var(--hy-element-padding);
            border-radius: var(--hy-element-border-radius);

            &.size-lg {
                top: calc(var(--hy-element-padding) + 2px); // 2px border-width
            }

            &.size-md {
                top: calc(var(--hy-element-padding-md) + 2px); // 2px border-width
            }

            &.size-sm {
                top: calc(var(--hy-element-padding-sm) + 2px); // 2px border-width
            }

            &.left {
                left: 2px; // 2px border-width
            }

            &.right {
                right: 2px; // 2px border-width
            }
        }
    }

    .invalidText {
        font-size: var(--hy-font-size-small);
        padding: var(--hy-element-padding-sm);
        margin: 0;
        background-color: var(--hy-invalid-bg-color);
        border-radius: var(--hy-element-border-radius);
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        position: relative;

        &::before,
        &::after {
            content: "";
            height: var(--hy-element-border-radius);
            width: var(--hy-element-border-radius);
            top: calc(var(--hy-element-border-radius) * -1);
            position: absolute;
            background-color: var(--hy-invalid-bg-color);
        }

        &::before {
            left: 0;
            clip-path: v-bind(invalidTextLeftClipPath);
        }

        &::after {
            right: 0;
            clip-path: v-bind(invalidTextRightClipPath);
        }
    }

    .hy-loader {
        position: absolute;
        top: calc(50% - 12.5px);
        left: calc(50% - 12.5px);
    }

    .hy-progress-bar {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

    &.invalidTextVisible {
        .hy-loader {
            top: calc(50% - 12.5px - (v-bind(invalidTextHeight) / 2));
        }

        .hy-progress-bar {
            top: calc(50% - (v-bind(invalidTextHeight) / 2));
        }
    }

    .invalidText,
    .iconSlot {
        &.muted {
            filter: opacity(50%);
        }
    }
}
</style>
