<template>
    <SMControl
        :class="['control-type-input', { 'input-active': active }]"
        :invalid="feedbackInvalid">
        <div v-if="slots.prepend" class="input-control-prepend">
            <slot name="prepend"></slot>
        </div>
        <div class="control-item">
            <label class="control-label" v-bind="{ for: id }">{{
                label
            }}</label>
            <ion-icon
                class="invalid-icon"
                name="alert-circle-outline"></ion-icon>
            <ion-icon
                v-if="props.showClear && value?.length > 0 && !feedbackInvalid"
                class="clear-icon"
                name="close-outline"
                @click.stop="handleClear"></ion-icon>
            <input
                :type="props.type"
                class="input-control"
                :disabled="disabled"
                v-bind="{ id: id, autofocus: props.autofocus }"
                v-model="value"
                @focus="handleFocus"
                @blur="handleBlur"
                @input="handleInput"
                @keyup="handleKeyup" />
        </div>
        <div v-if="slots.append" class="input-control-append">
            <slot name="append"></slot>
        </div>
        <template v-if="slots.help" #help><slot name="help"></slot></template>
    </SMControl>
</template>

<script setup lang="ts">
import { inject, watch, ref, useSlots } from "vue";
import { isEmpty } from "../helpers/utils";
import { toTitleCase } from "../helpers/string";
import SMControl from "./SMControl.vue";

const emits = defineEmits(["update:modelValue", "blur", "keyup"]);
const props = defineProps({
    form: {
        type: Object,
        default: undefined,
        required: false,
    },
    control: {
        type: [String, Object],
        default: "",
    },
    label: {
        type: String,
        default: undefined,
        required: false,
    },
    modelValue: {
        type: String,
        default: undefined,
        required: false,
    },
    type: {
        type: String,
        default: "text",
        required: false,
    },
    id: {
        type: String,
        default: undefined,
        required: false,
    },
    disabled: {
        type: Boolean,
        default: false,
        required: false,
    },
    button: {
        type: String,
        default: "",
        required: false,
    },
    showClear: {
        type: Boolean,
        default: false,
        required: false,
    },
    feedbackInvalid: {
        type: String,
        default: "",
        required: false,
    },
    autofocus: {
        type: Boolean,
        default: false,
        required: false,
    },
});

const slots = useSlots();

const form = inject("form", props.form);
const control =
    typeof props.control === "object"
        ? props.control
        : !isEmpty(form) &&
          typeof props.control === "string" &&
          props.control !== "" &&
          Object.prototype.hasOwnProperty.call(form.controls, props.control)
        ? form.controls[props.control]
        : null;

const label = ref(
    props.label != undefined
        ? props.label
        : typeof props.control == "string"
        ? toTitleCase(props.control)
        : ""
);
const value = ref(
    props.modelValue != undefined
        ? props.modelValue
        : control != null
        ? control.value
        : ""
);
const id = ref(
    props.id != undefined
        ? props.id
        : typeof props.control == "string"
        ? props.control
        : ""
);
const feedbackInvalid = ref(props.feedbackInvalid);
const active = ref(value.value?.length ?? 0 > 0);
const focused = ref(false);
const disabled = ref(props.disabled);

watch(
    () => value.value,
    (newValue) => {
        active.value = newValue.length > 0 || focused.value == true;
    }
);

watch(
    () => props.feedbackInvalid,
    (newValue) => {
        feedbackInvalid.value = newValue;
    }
);

if (typeof control === "object" && control !== null) {
    watch(
        () => control.validation.result.valid,
        (newValue) => {
            feedbackInvalid.value = newValue
                ? ""
                : control.validation.result.invalidMessages[0];
        },
        { deep: true }
    );

    watch(
        () => control.value,
        (newValue) => {
            value.value = newValue;
        },
        { deep: true }
    );
}

if (form) {
    watch(
        () => form.loading(),
        (newValue) => {
            disabled.value = newValue;
        }
    );
}

const handleFocus = () => {
    active.value = true;
    focused.value = true;
};

const handleBlur = async () => {
    active.value = value.value?.length ?? 0 > 0;
    focused.value = false;
    emits("blur");

    if (control) {
        await control.validate();
        control.isValid();
    }
};

const handleInput = (event: Event) => {
    const target = event.target as HTMLInputElement;
    value.value = target.value;
    emits("update:modelValue", target.value);

    if (control) {
        control.value = target.value;
        feedbackInvalid.value = "";
    }
};

const handleKeyup = (event: Event) => {
    emits("keyup", event);
};

const handleClear = () => {
    value.value = "";
    emits("update:modelValue", "");
    // emits("change");
};
</script>

<style lang="scss">
.control-group.control-type-input {
    .control-row {
        .control-item {
            align-items: start;
        }

        .input-control-prepend {
            p {
                display: block;
                color: var(--base-color-text);
                background-color: var(--base-color-dark);
                border-width: 1px 0 1px 1px;
                border-style: solid;
                border-color: var(--base-color-darker);
                border-radius: 8px 0 0 8px;
                padding: 16px 16px 16px 16px;
            }

            .button {
                border-width: 1px 0 1px 1px;
                border-style: solid;
                border-color: var(--base-color-darker);
                border-radius: 8px 0 0 8px;
            }

            & + .control-item .input-control {
                border-top-left-radius: 0;
                border-bottom-left-radius: 0;
            }
        }

        .input-control-append {
            p {
                display: block;
                color: var(--base-color-text);
                background-color: var(--base-color-dark);
                border-width: 1px 1px 1px 0;
                border-style: solid;
                border-color: var(--base-color-darker);
                border-radius: 0 8px 8px 0;
                padding: 16px 16px 16px 16px;
            }

            .button {
                border-width: 1px 1px 1px 0;
                border-style: solid;
                border-color: var(--base-color-darker);
                border-radius: 0 8px 8px 0;
            }
        }

        &:has(.control-item + .input-control-append)
            > .control-item
            .input-control {
            border-top-right-radius: 0;
            border-bottom-right-radius: 0;
        }

        .control-item {
            .control-label {
                position: absolute;
                display: block;
                transform-origin: top left;
                transform: translate(16px, 16px) scale(1);
                transition: all 0.1s ease-in-out;
                color: var(--base-color-darker);
                pointer-events: none;
            }

            .invalid-icon {
                position: absolute;
                display: none;
                right: 10px;
                top: 14px;
                color: var(--danger-color);
                font-size: 28px;
            }

            .clear-icon {
                position: absolute;
                right: 12px;
                top: 18px;
                background-color: #ccc;
                border-radius: 50%;
                font-size: 80%;
                padding: 1px 1px 1px 0px;

                &:hover {
                    color: #fff;
                }
            }

            .input-control {
                display: block;
                width: 100%;
                padding: 20px 16px 8px 16px;
                border: 1px solid var(--base-color-darker);
                border-radius: 8px;
                background-color: var(--base-color-light);
                color: var(--base-color-text);

                &:disabled {
                    background-color: hsl(0, 0%, 92%);
                    cursor: not-allowed;
                }
            }
        }
    }

    &.input-active {
        .control-item .control-label {
            transform: translate(16px, 6px) scale(0.7);
        }
    }

    &.control-invalid {
        .control-row .control-item {
            .invalid-icon {
                display: block;
            }

            .input-control {
                border: 2px solid var(--danger-color);
            }
        }
    }
}
</style>
