<template>
  <q-dialog :model-value="rewardModal">
    <q-card class="loyalty-suite-form__rewards-modal">
      <q-inner-loading :showing="loading">
        <q-spinner color="primary" size="70px" />
      </q-inner-loading>

      <q-form class="loyalty-suite-form" @submit="applyRewardsData">
        <q-card-section v-if="!loading">
          <template v-for="field in rewardsFields" :key="field.key">
            <div
              v-if="shouldShowField(field)"
              class="loyalty-suite-form__row loyalty-suite-form__row_full-width"
            >
              <span class="loyalty-suite-form__row-label">
                {{ $t(field.key) }}
                <span v-if="field.key === 'fixed_discount_value'" class="loyalty-suite-form__row-label-currency">
                  , {{ currency['symbol'] }}
                </span>
                <span
                  v-if="['percentage_discount_value'].includes(field.key)"
                  class="loyalty-suite-form__row-label-currency"
                >
                  , %
                </span>
                <span
                  v-if="requiredFields.includes(field.key)"
                  class="loyalty-suite-form__row-label-required"
                >*</span>
                <q-icon name="info_outline" size="18px" class="loyalty-suite-form__row-hint">
                  <q-tooltip anchor="top right" self="top right">
                    {{ field.key === 'reward' ? $t(`choose_${field.key}`) : $t(`opt_${field.key}`) }}
                  </q-tooltip>
                </q-icon>
              </span>

              <!-- primary -->
              <q-select
                v-if="field.type === 'primary'"
                v-model="tempRewardsData[field.key]"
                :options="filteredOptions[field.key] || field.options"
                option-disable="disabled"
                use-input
                hide-selected
                fill-input
                input-debounce="0"
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                :placeholder="$t('selectValue')"
                @filter="(val, update) => filterOptions(val, update, field)"
                outlined
                @update:model-value="(val) => {
                  if ((val?.__pro && !isPro) || (val?.__wc && !isWoo)) {
                    tempRewardsData[field.key] = null;
                  }
                }"
              >
                <template v-slot:no-option>
                  <q-item>
                    <q-item-section class="text-grey">{{ $t('noResultsFound') }}</q-item-section>
                  </q-item>
                </template>

                <template v-slot:option="scope">
                  <q-item
                    v-bind="(scope.opt.__pro && !isPro) || (scope.opt.__wc && !isWoo)
                      ? { ...scope.itemProps, onClick: undefined }
                      : scope.itemProps"
                    :class="{
                      'loyalty-suite-form__pro-field': scope.opt.__pro && !isPro,
                      'loyalty-suite-form__wc-field': scope.opt.__wc && !isWoo
                    }"
                  >
                    <q-item-section>
                      <q-item-label>{{ scope.opt.label }}</q-item-label>
                      <q-badge
                        v-if="scope.opt.__pro && !isPro"
                        color="positive"
                        label="PRO"
                      />
                    </q-item-section>
                    <q-item-section v-if="!scope.opt.disabled" side>
                      <q-icon
                        name="info_outline"
                        class="loyalty-suite-form__select-info"
                        size="18px"
                      >
                        <q-tooltip anchor="top right" self="top right">
                          {{ $t(`in_${scope.opt.value}`) }}
                        </q-tooltip>
                      </q-icon>
                    </q-item-section>
                  </q-item>
                </template>
              </q-select>

              <!-- number -->
              <q-input
                v-else-if="field.type === 'number'"
                v-model="tempRewardsData[field.key]"
                type="number"
                :placeholder="$t('enterValue')"
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                @update:model-value="tempRewardsData[field.key] = formatNumber(tempRewardsData[field.key])"
                outlined
              />

              <!-- percent -->
              <q-input
                v-else-if="field.type === 'percent'"
                v-model="tempRewardsData[field.key]"
                type="number"
                :placeholder="$t('enterValue')"
                :rules="[
                  () => !requiredFields.includes(field.key)
                    || !!tempRewardsData[field.key]
                    || $t('requiredField'),
                  () => tempRewardsData[field.key] <= 100
                    || $t('percentFieldRule', { value: 100 })
                ]"
                @update:model-value="tempRewardsData[field.key] = formatNumber(tempRewardsData[field.key])"
                outlined
              />

              <!-- single -->
              <q-select
                v-else-if="field.type === 'single'"
                v-model="tempRewardsData[field.key]"
                :options="filteredOptions[field.key] || field.options"
                option-disable="disabled"
                use-input
                hide-selected
                fill-input
                input-debounce="0"
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                :placeholder="$t('selectValue')"
                @filter="(val, update) => filterOptions(val, update, field)"
                outlined
              >
                <template v-slot:no-option>
                  <q-item>
                    <q-item-section class="text-grey">{{ $t('noResultsFound') }}</q-item-section>
                  </q-item>
                </template>
              </q-select>

              <!-- simple_select -->
              <q-select
                class="loyalty-suite-simple-select"
                v-else-if="field.type === 'simple_select'"
                v-model="tempRewardsData[field.key]"
                :options="filteredOptions[field.key] || field.options"
                option-disable="disabled"
                input-debounce="0"
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                :placeholder="$t('selectValue')"
                @filter="(val, update) => filterOptions(val, update, field)"
                outlined
              >
                <template v-slot:no-option>
                  <q-item>
                    <q-item-section class="text-grey">{{ $t('noResultsFound') }}</q-item-section>
                  </q-item>
                </template>
              </q-select>

              <!-- single_clearable -->
              <q-select
                v-else-if="field.type === 'single_clearable'"
                v-model="tempRewardsData[field.key]"
                :options="filteredOptions[field.key] || field.options"
                option-disable="disabled"
                clearable
                input-debounce="0"
                use-input
                hide-selected
                fill-input
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                :placeholder="$t('selectValue')"
                @filter="(val, update) => filterOptions(val, update, field)"
                outlined
                :display-value="!tempRewardsData[field.key] ? $t('selectValue') : ''"
              >
                <template v-slot:no-option>
                  <q-item>
                    <q-item-section class="text-grey">{{ $t('noResultsFound') }}</q-item-section>
                  </q-item>
                </template>
              </q-select>

              <!-- dependency_single -->
              <q-select
                v-else-if="field.type === 'dependency_single'"
                v-model="tempRewardsData[field.key]"
                :options="filteredOptions[field.key] || field.options"
                option-disable="disabled"
                use-input
                fill-input
                hide-selected
                outlined
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                :placeholder="$t('selectValue')"
                :loading="loadingStates[field.key]"
              >
                <template v-slot:no-option>
                  <q-item>
                    <q-item-section class="text-grey">{{ $t('noResultsFound') }}</q-item-section>
                  </q-item>
                </template>
              </q-select>

              <!-- api_multiselect -->
              <q-select
                v-else-if="field.type === 'api_multiselect'"
                v-model="tempRewardsData[field.key]"
                :options="filteredOptions[field.key] || field.options"
                option-disable="disabled"
                use-chips
                use-input
                fill-input
                multiple
                input-debounce="300"
                :placeholder="$t('enterValue')"
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                @filter="(val, update) => fetchOptions(val, update, field)"
                @update:model-value="checkMultiSelect(field.key)"
                outlined
              >
                <template v-slot:no-option>
                  <q-item v-if="!isFiltering && searchQuery.length >= 2">
                    <q-item-section class="text-grey">{{ $t('noResultsFound') }}</q-item-section>
                  </q-item>
                </template>
              </q-select>

              <!-- datepicker -->
              <q-input
                v-else-if="field.type === 'datepicker'"
                v-model="formattedDate"
                outlined
                readonly
                :rules="requiredFields.includes(field.key)
                  ? [() => !!tempRewardsData[field.key] || $t('requiredField')]
                  : []"
                :placeholder="$t('selectValue')"
                class="loyalty-suite-form__datepicker"
                @click="openDatePicker(field.key)"
              >
                <template v-slot:append>
                  <div v-if="formattedDate" class="loyalty-suite-form__datepicker-clear">
                    <q-icon
                      name="close"
                      class="cursor-pointer"
                      @click.prevent="clearDate(field.key)"
                    />
                  </div>
                  <q-icon
                    name="event"
                    :data-datepicker="field.key"
                    class="cursor-pointer"
                  >
                    <q-popup-proxy
                      v-model="datePickerVisible"
                      cover
                      transition-show="scale"
                      transition-hide="scale"
                    >
                      <q-date
                        v-model="tempRewardsData[field.key]"
                        :mask="dateMask"
                        :locale="datePickerLocale"
                        :first-day-of-week="wpStartOfWeek"
                        :options="disablePastDates"
                        @update:model-value="(val) => updateFormattedDate(val)"
                      >
                        <div class="row items-center justify-end">
                          <q-btn v-close-popup :label="$t('close')" color="primary" />
                        </div>
                      </q-date>
                    </q-popup-proxy>
                  </q-icon>
                </template>
              </q-input>
            </div>
          </template>
        </q-card-section>

        <q-card-actions v-if="!loading" align="center">
          <q-btn :label="$t('cancel')" color="negative" @click="resetForm" />
          <q-btn type="submit" :label="initialData ? $t('edit') : $t('add')" color="primary" />
        </q-card-actions>
      </q-form>
    </q-card>
  </q-dialog>
</template>

<script lang="ts">
import { defineComponent, ref, watch, toRaw, computed, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';

export default defineComponent({
  props: {
    rewardModal: {
      type: Boolean,
      required: true,
    },
    initialData: {
      type: Object as () => Record<string, any> | null,
      default: null,
    },
    rewardsFields: {
      type: Array as () => Array<{
        key: string;
        type: string;
        options?: { value: string; label: string }[];
        dependency?: string[];
      }>,
      required: true,
    },
    requiredFields: {
      type: Array as () => string[],
      required: true,
    },
    loading: {
      type: Boolean,
      required: true,
    },
    currency: {
      type: Object as () => Record<string, any>,
      required: true,
    },
  },
  emits: ['update:rewardModal', 'add-reward', 'update-reward'],
  setup(props, { emit }) {
    const apiRoutes = window.loyalty_suite_admin_data.apiRoutes;
    const nonce = window.loyalty_suite_admin_data.nonce;
    const tempRewardsData = ref<Record<string, any>>({});
    const loadingStates = ref<Record<string, boolean>>({});
    const wpDateFormat = ref<string>(window.loyalty_suite_admin_data?.dateFormat || 'Y-m-d');
    const formattedDate = ref<string | null>(null);
    const wpStartOfWeek = window.loyalty_suite_admin_data?.startOfWeek ?? 1;
    const isPro = window.loyalty_suite_admin_data.isPro;
    const isWoo = window.loyalty_suite_admin_data.isWooEnabled;
    const datePickerVisible = ref(false);
    const searchQuery = ref('');
    const isFiltering = ref(false);
    const { t } = useI18n();

    const dateMask = 'YYYY-MM-DD';

    const datePickerLocale = {
      days: [
        t('Sunday'),
        t('Monday'),
        t('Tuesday'),
        t('Wednesday'),
        t('Thursday'),
        t('Friday'),
        t('Saturday'),
      ],
      daysShort: [
        t('Sun'),
        t('Mon'),
        t('Tue'),
        t('Wed'),
        t('Thu'),
        t('Fri'),
        t('Sat'),
      ],
      months: [
        t('January'),
        t('February'),
        t('March'),
        t('April'),
        t('May'),
        t('June'),
        t('July'),
        t('August'),
        t('September'),
        t('October'),
        t('November'),
        t('December'),
      ],
      monthsShort: [
        t('Jan'),
        t('Feb'),
        t('Mar'),
        t('Apr'),
        t('MayShort'),
        t('Jun'),
        t('Jul'),
        t('Aug'),
        t('Sep'),
        t('Oct'),
        t('Nov'),
        t('Dec'),
      ],
    };

    const normalizeApiDateToYmd = (value: string | null | undefined): string => {
      if (!value) return '';
      const trimmed = String(value).trim();
      const match = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})/);
      if (!match) return '';
      const [, y, m, d] = match;
      return `${y}-${m}-${d}`;
    };

    const formatDateLabel = (dateStr: string): string => {
      if (!dateStr) return '';
      const parts = dateStr.split('-');
      if (parts.length !== 3) return dateStr;

      const year = Number(parts[0]);
      const monthIndex = Number(parts[1]) - 1;
      const day = Number(parts[2]);

      if (!year || monthIndex < 0 || monthIndex > 11 || !day) {
        return dateStr;
      }

      const monthName = datePickerLocale.months[monthIndex] ?? '';

      switch (wpDateFormat.value) {
        case 'F j, Y':
          return `${monthName} ${day}, ${year}`;
        case 'Y-m-d':
          return `${year}-${String(monthIndex + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
        case 'm/d/Y':
          return `${String(monthIndex + 1).padStart(2, '0')}/${String(day).padStart(2, '0')}/${year}`;
        case 'd/m/Y':
          return `${String(day).padStart(2, '0')}/${String(monthIndex + 1).padStart(2, '0')}/${year}`;
        case 'Y/m/d':
          return `${year}/${String(monthIndex + 1).padStart(2, '0')}/${String(day).padStart(2, '0')}`;
        case 'd.m.Y':
          return `${String(day).padStart(2, '0')}.${String(monthIndex + 1).padStart(2, '0')}.${year}`;
        case 'j. F Y':
          return `${day}. ${monthName} ${year}`;
        case 'j F Y':
          return `${day} ${monthName} ${year}`;
        default:
          return `${year}-${String(monthIndex + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
      }
    };

    const disablePastDates = (date: string) => {
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      const selectedDate = new Date(date);
      return selectedDate >= today;
    };

    const clearDate = (key: string) => {
      tempRewardsData.value[key] = null;
      if (key === 'rewards_calendar_date') {
        formattedDate.value = '';
      }
    };

    const updateFormattedDate = (date: string | null) => {
      const normalized = normalizeApiDateToYmd(date || '');
      if (normalized) {
        tempRewardsData.value.rewards_calendar_date = normalized;
        formattedDate.value = formatDateLabel(normalized);
      } else {
        tempRewardsData.value.rewards_calendar_date = null;
        formattedDate.value = '';
      }
      datePickerVisible.value = false;
    };

    const openDatePicker = (key: string) => {
      if (key !== 'rewards_calendar_date') return;

      nextTick(() => {
        const popup = document.querySelector(`[data-datepicker="${key}"]`);
        if (popup) {
          (popup as HTMLElement).click();
        }
      });
    };

    const shouldShowField = (field: any): boolean => {
      if (!Array.isArray(field.dependency) || field.dependency.length === 0) {
        return true;
      }

      const rewardsData = tempRewardsData.value || {};

      return field.dependency.every((dep: any) => {
        if (!dep || typeof dep !== 'object' || !dep.key) {
          return false;
        }

        const rewardValue = rewardsData[dep.key];

        if (Array.isArray(dep.value)) {
          return rewardValue !== undefined && dep.value.includes(rewardValue?.value);
        }

        if (dep.value === true) {
          return rewardValue !== null && rewardValue !== undefined;
        }

        return false;
      });
    };

    const formatNumber = (value: number | string | null): number | null => {
      if (value === null || value === '' || isNaN(Number(value))) {
        return null;
      }

      const numberValue = Math.round(Number(value));

      return numberValue > 0 ? numberValue : null;
    };

    const filteredOptions = ref<
      Record<string, { value: string; label: string; disabled?: boolean }[]>
    >({});

    const filterOptions = (
      val: string,
      update: (callback: () => void) => void,
      field: { key: string; options?: { value: string; label: string; disabled?: boolean }[] }
    ) => {
      const fieldOptions = field.options ?? [];

      update(() => {
        filteredOptions.value[field.key] = val
          ? fieldOptions.filter(
              (option) =>
                !option.disabled &&
                option.label.toLowerCase().includes(val.toLowerCase())
            )
          : [...fieldOptions];
      });
    };

    const fetchOptions = async (
      val: string,
      update: (callback: () => void) => void,
      field: any
    ) => {
      searchQuery.value = val;

      if (!val) {
        filteredOptions.value[field.key] = [];
        isFiltering.value = false;
        update(() => {});
        return;
      }

      isFiltering.value = true;

      try {
        const buildApiUrl = (base, params = {}) => {
          const url = new URL(base, window.location.origin);
          Object.entries(params).forEach(([key, value]) => {
            if (value !== undefined && value !== null && value !== '') {
              url.searchParams.set(key, value as string);
            }
          });
          return url.toString();
        };

        const response = await fetch(
          buildApiUrl(apiRoutes.admin[field.key], { identifier: val }),
          {
            method: 'GET',
            headers: {
              'X-WP-Nonce': nonce,
              'Content-Type': 'application/json',
            },
            credentials: 'same-origin',
          }
        );

        if (!response.ok) {
          return [];
        }

        const data = await response.json();

        update(() => {
          filteredOptions.value[field.key] = Array.isArray(data) ? data : [];
          isFiltering.value = false;
        });
      } catch (error) {
        console.error(t('couldNotFetchData'), error);
        update(() => {
          filteredOptions.value[field.key] = [];
          isFiltering.value = false;
        });
      }
    };

    const checkMultiSelect = (key: string) => {
      if (
        Array.isArray(tempRewardsData.value[key]) &&
        tempRewardsData.value[key].length === 0
      ) {
        tempRewardsData.value[key] = null;
      }
    };

    watch(
      () => tempRewardsData.value.reward,
      async (newReward) => {
        if (!newReward) return;

        const rewardValue = newReward;

        tempRewardsData.value = {};
        formattedDate.value = '';
        tempRewardsData.value.reward = rewardValue;

        props.rewardsFields.forEach((field) => {
          if (!shouldShowField(field)) return;
          if (
            Array.isArray(field.options) &&
            typeof field.options[0] === 'object'
          ) {
            const options =
              filteredOptions.value[field.key] || field.options || [];
            const defaultOption = options.find(
              (opt) => opt.value === (field as any).default
            );
            if (defaultOption) {
              tempRewardsData.value[field.key] = defaultOption;
            }
          } else {
            tempRewardsData.value[field.key] = (field as any).default;
          }
        });

        await nextTick();
        Object.keys(tempRewardsData.value).forEach((key) => {
          checkMultiSelect(key);
        });
      }
    );

    watch(
      () => props.initialData,
      async (newData) => {
        if (!newData) {
          tempRewardsData.value = {};
          formattedDate.value = '';
          return;
        }

        tempRewardsData.value = {};

        if (newData.reward) {
          tempRewardsData.value.reward = JSON.parse(
            JSON.stringify(newData.reward)
          );
        }

        await nextTick();

        Object.keys(newData).forEach((key) => {
          if (key !== 'reward' && key !== 'rewards_calendar_date') {
            tempRewardsData.value[key] = JSON.parse(
              JSON.stringify((newData as any)[key])
            );
          }
        });

        if ((newData as any).rewards_calendar_date) {
          const normalized = normalizeApiDateToYmd(
            (newData as any).rewards_calendar_date
          );
          tempRewardsData.value.rewards_calendar_date = normalized || null;
          formattedDate.value = normalized
            ? formatDateLabel(normalized)
            : '';
        } else {
          formattedDate.value = '';
        }

        Object.keys(tempRewardsData.value).forEach((key) => {
          checkMultiSelect(key);
        });
      },
      { deep: true, immediate: true }
    );

    watch(
      () => tempRewardsData.value.rewards_period_type,
      (newVal) => {
        const type =
          typeof newVal === 'object' && newVal !== null ? newVal.value : newVal;

        if (type === 'rewards_calendar_date') {
          tempRewardsData.value.rewards_days_period = null;
        } else if (type === 'rewards_days_period') {
          tempRewardsData.value.rewards_calendar_date = null;
          formattedDate.value = '';
        } else {
          tempRewardsData.value.rewards_days_period = null;
          tempRewardsData.value.rewards_calendar_date = null;
          formattedDate.value = '';
        }
      }
    );

    watch(
      () => tempRewardsData.value.rewards_rank_set,
      async (newRankSet, oldRankSet) => {
        const fieldKey = 'rewards_rank';

        if (!newRankSet || !newRankSet.value) {
          filteredOptions.value[fieldKey] = [];
          return;
        }

        loadingStates.value[fieldKey] = true;
        filteredOptions.value[fieldKey] = await loadRanksForSet(newRankSet.value);
        loadingStates.value[fieldKey] = false;

        if (oldRankSet !== undefined && newRankSet !== oldRankSet) {
          tempRewardsData.value[fieldKey] = null;
        }
      },
      { immediate: true }
    );

    const loadRanksForSet = async (rankSetId: string) => {
      try {
        const buildApiUrl = (base, params = {}) => {
          const url = new URL(base, window.location.origin);
          Object.entries(params).forEach(([key, value]) => {
            if (value !== undefined && value !== null && value !== '') {
              url.searchParams.set(key, value as string);
            }
          });
          return url.toString();
        };

        const response = await fetch(
          buildApiUrl(apiRoutes.admin.rewards_ranks, { set_id: rankSetId }),
          {
            method: 'GET',
            headers: {
              'X-WP-Nonce': nonce,
              'Content-Type': 'application/json',
            },
            credentials: 'same-origin',
          }
        );

        if (!response.ok) {
          return [];
        }

        return await response.json();
      } catch (error) {
        console.error('Error fetching ranks:', error);
        return [];
      }
    };

    const resetForm = async () => {
      emit('update:rewardModal', false);
      await nextTick();

      if (!props.initialData) {
        tempRewardsData.value = {};
        formattedDate.value = '';
      }
    };

    const applyRewardsData = () => {
      const rawData = toRaw(tempRewardsData.value);
      const updatedData = props.initialData ? { ...props.initialData } : {};

      Object.keys(updatedData).forEach((key) => {
        if (key !== 'reward') {
          delete (updatedData as any)[key];
        }
      });

      Object.assign(updatedData, rawData);

      if (props.initialData) {
        emit('update-reward', updatedData);
      } else {
        emit('add-reward', updatedData);
      }

      emit('update:rewardModal', false);
      tempRewardsData.value = {};
      formattedDate.value = '';
    };

    return {
      tempRewardsData,
      datePickerLocale,
      wpStartOfWeek,
      disablePastDates,
      datePickerVisible,
      updateFormattedDate,
      formattedDate,
      openDatePicker,
      clearDate,
      formatNumber,
      filterOptions,
      fetchOptions,
      filteredOptions,
      checkMultiSelect,
      loadingStates,
      resetForm,
      applyRewardsData,
      shouldShowField,
      searchQuery,
      isFiltering,
      isPro,
      isWoo,
      dateMask,
    };
  },
});
</script>
