<template>
  <widget-wrapper
    v-bind="$props"
    v-show="!isHidden"
    class="checkbox"
    v-slot="{ inputEvents }"
  >
    <q-checkbox
      ref="checkbox"
      :value="currentValue"
      :color="hasError ? 'negative' : 'primary'"
      :disable="disable"
      :keep-color="hasError"
      @input="onInput(inputEvents, $event)"
      :left-label="!rightLabel"
      :label="fieldNode.label"
      class="q-mx-md"
      :class="{ 'q-ml-lg': !rightLabel }"
    />
    <div
      class="q-field__inner relative-position col self-stretch column justify-center error-container"
      :class="[rightLabel ? 'q-ml-md' : 'q-ml-xs']"
    >
      <transition name="errorMessage">
        <div
          v-show="hasError"
          class="row items-start text-negative error-message"
        >
          {{ errorMessage }}
        </div>
      </transition>
    </div>
  </widget-wrapper>
</template>

<script lang="ts">
import { Component, Prop, Watch } from 'vue-property-decorator';
import { mixins } from 'vue-class-component';
import {
  ValidatedField,
  FormWidget,
  ObjectMetaModelValue,
  WidgetWrapper,
} from '@devsun/form-generator/dist';
import { QCheckbox, QTooltip } from 'quasar';
import get from 'lodash/get';

declare type customBoolType = number | string | boolean | null;

export enum ValidBoolTypes {
  string = 'string',
  number = 'number',
  boolean = 'boolean',
  custom = 'custom',
}

export interface CustomBool {
  trueValue?: customBoolType;
  falseValue?: customBoolType;
}

@Component({
  components: {
    QCheckbox,
    QTooltip,
    WidgetWrapper,
  },
})
export default class Checkbox extends mixins(FormWidget, ValidatedField) {
  @Prop({ default: false }) private rightLabel: boolean;
  @Prop({ default: null }) private customType: CustomBool | null;
  @Prop({}) hideNulls!: boolean;

  private initialBoolType: ValidBoolTypes | null = null;

  private static bools = {
    [ValidBoolTypes.string]: {
      true: true,
      false: false,
    },
    [ValidBoolTypes.number]: {
      0: false,
      1: true,
    },
  };

  protected get customTrueValue(): customBoolType {
    return get(this, 'customType.trueValue', true);
  }

  protected get customFalseValue(): customBoolType {
    return get(this, 'customType.falseValue', false);
  }

  get isClearModalOpen() {
    return this.$store.getters['formGenerator/isClearModalOpen'];
  }

  get disable() {
    return this.fieldNode.readonly || this.isClearModalOpen;
  }

  private setBool(type: ValidBoolTypes, value: string) {
    switch (type) {
      case 'boolean':
        return value;
      case 'number':
        return value ? 1 : 0;
      case 'string':
        return value ? 'true' : 'false';
      case 'custom':
        return value ? this.customTrueValue : this.customFalseValue;
      default:
        return null;
    }
  }

  private getBool(type: ValidBoolTypes, value: any): boolean | null {
    if (type === ValidBoolTypes.boolean) {
      return Boolean(value) as boolean;
    }
    if (this.customType) {
      return this.customTypeGetter(value);
    }
    const res = Checkbox.bools[type]
      ? Checkbox.bools[type][value.toString()]
      : false;
    return res !== void 0 ? res : false;
  }

  private customTypeGetter(value: any): boolean {
    switch (`${value}`) {
      case `${this.customTrueValue}`:
        return true;
      case `${this.customFalseValue}`:
        return false;
      default:
        return false;
    }
  }

  private get currentValue(): boolean | null {
    return this.getBool(this.getBoolTypeByValue(this.value), this.value.__id);
  }

  private setValue(val) {
    return this.setBool(this.initialBoolType, val);
  }

  getBoolTypeByValue(value: ObjectMetaModelValue) {
    if (this.customType) {
      return ValidBoolTypes.custom;
    }
    // by default consider it boolean
    const comparator = value.__id === '' ? true : value.__id;

    switch (typeof comparator) {
      case 'string':
        return ValidBoolTypes.string;
      case 'number':
        return ValidBoolTypes.number;
      case 'boolean':
        return ValidBoolTypes.boolean;
      default:
        throw new Error(
          `wrong type specified for Checkbox boolean for '${this.fieldNode.fieldName}' `
        );
    }
  }

  created() {
    this.initialBoolType = this.getBoolTypeByValue(this.value);
  }

  mounted() {
    this.setDefault();
  }

  setDefault() {
    if (this.value.__id) return;

    const defaultVal = this.fieldNode?.rawAttrs?.props?.setDefault;
    if (defaultVal === undefined) return;

    this.$refs.checkbox.$emit('input', defaultVal);
  }

  get isHidden(): boolean {
    return (
      (this.hideNulls && this.currentValue === null) ||
      (this.fieldNode?.rawAttrs?.props?.hideWhenFalse &&
        this.currentValue === false)
    );
  }
  
  onInput(inputEvents: any, e: Event) {
    this.$store.commit('formGenerator/setClearModal', true);
    if (!this.isClearModalOpen) {
      inputEvents.input(this.setValue(e))
    }
  }
}
</script>

<style scoped>
.checkbox {
  margin-top: 12px;
}
.error-container {
  height: 12px;
}
.error-message {
  font-size: 12px;
  min-height: 12px;
  line-height: 1;
  color: rgba(0, 0, 0, 0.54);
  padding: 0 12px;
}
.errorMessage-enter-active,
.errorMessage-leave-active {
  transition: transform 0.3s linear, opacity 0.3s linear;
}
.errorMessage-enter-to,
.errorMessage-leave {
  opacity: 1;
  transform: translateY(0);
}
.errorMessage-leave-to,
.errorMessage-enter {
  opacity: 0;
  transform: translateY(-100%);
}
</style>
