<template>
  <v-container>
    <BasePageTitle divider>
      <template #header>
        <v-img
          width="100"
          max-height="100"
          max-width="100"
          src="/svgs/manage-cookbooks.svg"
        />
      </template>
      <template #title>
        {{ $t('recipe-finder.recipe-finder') }}
      </template>

      <template #content>
        {{ $t('recipe-finder.recipe-finder-description') }}
      </template>
    </BasePageTitle>
    <v-container v-if="ready">
      <v-row>
        <v-col :cols="useMobile ? 12 : 3">
          <v-container class="ma-0 pa-0">
            <v-row no-gutters>
              <v-col
                cols="12"
                no-gutters
                :class="attrs.searchFilter.colClass"
              >
                <SearchFilter
                  v-if="foods"
                  v-model="selectedFoods"
                  :items="foods"
                  :class="attrs.searchFilter.filterClass"
                >
                  <v-icon start>
                    {{ $globals.icons.foods }}
                  </v-icon>
                  {{ $t("general.foods") }}
                </SearchFilter>
                <SearchFilter
                  v-if="tools"
                  v-model="selectedTools"
                  :items="tools"
                  :class="attrs.searchFilter.filterClass"
                >
                  <v-icon start>
                    {{ $globals.icons.potSteam }}
                  </v-icon>
                  {{ $t("tool.tools") }}
                </SearchFilter>
                <div :class="attrs.searchFilter.filterClass">
                  <v-badge
                    :model-value="!!queryFilterJSON.parts && queryFilterJSON.parts.length > 0"
                    size="small"
                    color="primary"
                    :content="(queryFilterJSON.parts || []).length"
                  >
                    <v-btn
                      size="small"
                      color="accent"
                      dark
                      @click="queryFilterMenu = !queryFilterMenu"
                    >
                      <v-icon start>
                        {{ $globals.icons.filter }}
                      </v-icon>
                      {{ $t("recipe-finder.other-filters") }}
                      <BaseDialog
                        v-model="queryFilterMenu"
                        :title="$t('recipe-finder.other-filters')"
                        :icon="$globals.icons.filter"
                        width="100%"
                        max-width="1100px"
                        :submit-disabled="!queryFilterEditorValue"
                        can-confirm
                        @confirm="saveQueryFilter"
                      >
                        <v-card-text>
                          <QueryFilterBuilder
                            :key="queryFilterMenuKey"
                            :initial-query-filter="queryFilterJSON"
                            :field-defs="queryFilterBuilderFields"
                            @input="(value) => queryFilterEditorValue = value"
                            @input-j-s-o-n="(value) => queryFilterEditorValueJSON = value"
                          />
                        </v-card-text>
                        <template #custom-card-action>
                          <BaseButton
                            color="error"
                            type="submit"
                            @click="clearQueryFilter"
                          >
                            <template #icon>
                              {{ $globals.icons.close }}
                            </template>
                            {{ $t("search.clear-selection") }}
                          </BaseButton>
                        </template>
                      </BaseDialog>
                    </v-btn>
                  </v-badge>
                </div>
              </v-col>
            </v-row>
            <!-- Settings Menu -->
            <v-row
              no-gutters
              class="mb-2"
            >
              <v-col
                cols="12"
                :class="attrs.settings.colClass"
              >
                <v-menu
                  v-model="settingsMenu"
                  offset-y
                  nudge-bottom="3"
                  :close-on-content-click="false"
                >
                  <template #activator="{ props }">
                    <v-btn
                      size="small"
                      color="primary"
                      dark
                      v-bind="props"
                    >
                      <v-icon start>
                        {{ $globals.icons.cog }}
                      </v-icon>
                      {{ $t("general.settings") }}
                    </v-btn>
                  </template>
                  <v-card>
                    <v-card-text>
                      <div>
                        <v-number-input
                          v-model="settings.maxMissingFoods"
                          :precision="null"
                          :min="0"
                          control-variant="stacked"
                          inset
                          hide-details
                          :label="$t('recipe-finder.max-missing-ingredients')"
                        />
                        <v-number-input
                          v-model="settings.maxMissingTools"
                          :precision="null"
                          :min="0"
                          control-variant="stacked"
                          inset
                          hide-details
                          :label="$t('recipe-finder.max-missing-tools')"
                          class="mt-4"
                        />
                      </div>
                      <div class="mt-1">
                        <v-checkbox
                          v-if="isOwnGroup"
                          v-model="settings.includeFoodsOnHand"
                          density="compact"
                          size="small"
                          hide-details
                          class="my-auto"
                          :label="$t('recipe-finder.include-ingredients-on-hand')"
                        />
                        <v-checkbox
                          v-if="isOwnGroup"
                          v-model="settings.includeToolsOnHand"
                          density="compact"
                          size="small"
                          hide-details
                          class="my-auto"
                          :label="$t('recipe-finder.include-tools-on-hand')"
                        />
                      </div>
                    </v-card-text>
                  </v-card>
                </v-menu>
              </v-col>
            </v-row>
            <v-row
              no-gutters
              class="my-2"
            >
              <v-col cols="12">
                <v-divider />
              </v-col>
            </v-row>
            <v-row
              no-gutters
              class="mt-5"
            >
              <v-card-title class="ma-0 pa-0">
                {{ $t("recipe-finder.selected-ingredients") }}
              </v-card-title>
              <v-container
                class="ma-0 pa-0"
                style="max-height: 60vh; overflow-y: auto;"
              >
                <v-card-text
                  v-if="!selectedFoods.length"
                  class="ma-0 pa-0"
                >
                  {{ $t("recipe-finder.no-ingredients-selected") }}
                </v-card-text>
                <div v-if="useMobile">
                  <v-row no-gutters>
                    <v-col
                      cols="12"
                      class="d-flex flex-wrap justify-end"
                    >
                      <v-chip
                        v-for="food in selectedFoods"
                        :key="food.id"
                        label
                        class="ma-1"
                        color="accent custom-transparent"
                        closable
                        variant="flat"
                        @click:close="removeFood(food)"
                      >
                        <span class="text-hide-overflow">{{ food.pluralName || food.name }}</span>
                      </v-chip>
                    </v-col>
                  </v-row>
                </div>
                <div v-else>
                  <v-row
                    v-for="food in selectedFoods"
                    :key="food.id"
                    no-gutters
                    class="mb-1"
                  >
                    <v-col cols="12">
                      <v-chip
                        label
                        color="accent custom-transparent"
                        variant="flat"
                        closable
                        @click:close="removeFood(food)"
                      >
                        <span class="text-hide-overflow">{{ food.pluralName || food.name }}</span>
                      </v-chip>
                    </v-col>
                  </v-row>
                </div>
              </v-container>
            </v-row>
            <v-row
              v-if="selectedTools.length"
              no-gutters
              class="mt-5"
            >
              <v-card-title class="ma-0 pa-0">
                {{ $t("recipe-finder.selected-tools") }}
              </v-card-title>
              <v-container class="ma-0 pa-0">
                <div v-if="useMobile">
                  <v-row no-gutters>
                    <v-col
                      cols="12"
                      class="d-flex flex-wrap justify-end"
                    >
                      <v-chip
                        v-for="tool in selectedTools"
                        :key="tool.id"
                        label
                        class="ma-1"
                        color="accent custom-transparent"
                        closeable
                        variant="flat"
                        @click:close="removeTool(tool)"
                      >
                        <span class="text-hide-overflow">{{ tool.name }}</span>
                      </v-chip>
                    </v-col>
                  </v-row>
                </div>
                <div v-else>
                  <v-row
                    v-for="tool in selectedTools"
                    :key="tool.id"
                    no-gutters
                    class="mb-1"
                  >
                    <v-col cols="12">
                      <v-chip
                        label
                        color="accent custom-transparent"
                        closable
                        variant="flat"
                        @click:close="removeTool(tool)"
                      >
                        <span class="text-hide-overflow">{{ tool.name }}</span>
                      </v-chip>
                    </v-col>
                  </v-row>
                </div>
              </v-container>
            </v-row>
          </v-container>
        </v-col>
        <v-col
          :cols="useMobile ? 12 : 9"
          :style="useMobile ? '' : 'max-height: 70vh; overflow-y: auto'"
        >
          <v-container
            v-if="recipeSuggestions.readyToMake.length || recipeSuggestions.missingItems.length"
            class="ma-0 pa-0"
          >
            <v-row
              v-if="recipeSuggestions.readyToMake.length"
              density="compact"
            >
              <v-col cols="12">
                <v-card-title :class="attrs.title.class.readyToMake">
                  {{ $t("recipe-finder.ready-to-make") }}
                </v-card-title>
              </v-col>
              <v-col
                v-for="(item, idx) in recipeSuggestions.readyToMake"
                :key="`${idx}-ready`"
                cols="12"
              >
                <v-lazy>
                  <RecipeSuggestion
                    :recipe="item.recipe"
                    :missing-foods="item.missingFoods"
                    :missing-tools="item.missingTools"
                    :disable-checkbox="loading"
                    @add-food="addFood"
                    @remove-food="removeFood"
                    @add-tool="addTool"
                    @remove-tool="removeTool"
                  />
                </v-lazy>
              </v-col>
            </v-row>
            <v-row
              v-if="recipeSuggestions.missingItems.length"
              density="compact"
            >
              <v-col cols="12">
                <v-card-title :class="attrs.title.class.missingItems">
                  {{ $t("recipe-finder.almost-ready-to-make") }}
                </v-card-title>
              </v-col>
              <v-col
                v-for="(item, idx) in recipeSuggestions.missingItems"
                :key="`${idx}-missing`"
                cols="12"
              >
                <v-lazy>
                  <RecipeSuggestion
                    :recipe="item.recipe"
                    :missing-foods="item.missingFoods"
                    :missing-tools="item.missingTools"
                    :disable-checkbox="loading"
                    @add-food="addFood"
                    @remove-food="removeFood"
                    @add-tool="addTool"
                    @remove-tool="removeTool"
                  />
                </v-lazy>
              </v-col>
            </v-row>
          </v-container>
          <v-container v-else-if="!recipesReady">
            <v-row>
              <v-col
                cols="12"
                class="d-flex justify-center"
              >
                <div class="text-center">
                  <AppLoader waiting-text="" />
                </div>
              </v-col>
            </v-row>
          </v-container>
          <v-container v-else>
            <v-row>
              <v-col
                cols="12"
                class="d-flex flex-column justify-center align-center ga-1"
              >
                <v-card-title class="ma-0 pa-0">
                  {{ $t("recipe-finder.no-recipes-found") }}
                </v-card-title>
                <v-card-text class="ma-0 pa-0 text-center">
                  {{ $t("recipe-finder.no-recipes-found-description") }}
                </v-card-text>
              </v-col>
            </v-row>
          </v-container>
        </v-col>
      </v-row>
    </v-container>
    <v-container v-else>
      <v-row>
        <v-col
          cols="12"
          class="d-flex justify-center"
        >
          <div class="text-center">
            <AppLoader waiting-text="" />
          </div>
        </v-col>
      </v-row>
    </v-container>
  </v-container>
</template>

<script lang="ts">
import { watchDebounced } from "@vueuse/core";
import { useUserApi } from "~/composables/api";
import { usePublicExploreApi } from "~/composables/api/api-client";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import { useFoodStore, usePublicFoodStore, useToolStore, usePublicToolStore } from "~/composables/store";
import type { IngredientFood, RecipeSuggestionQuery, RecipeSuggestionResponseItem, RecipeTool } from "~/lib/api/types/recipe";
import { Organizer } from "~/lib/api/types/non-generated";
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
import RecipeSuggestion from "~/components/Domain/Recipe/RecipeSuggestion.vue";
import SearchFilter from "~/components/Domain/SearchFilter.vue";
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
import { useRecipeFinderPreferences } from "~/composables/use-users/preferences";

interface RecipeSuggestions {
  readyToMake: RecipeSuggestionResponseItem[];
  missingItems: RecipeSuggestionResponseItem[];
}

export default defineNuxtComponent({
  components: { QueryFilterBuilder, RecipeSuggestion, SearchFilter },
  setup() {
    const display = useDisplay();
    const i18n = useI18n();
    const $auth = useMealieAuth();
    const route = useRoute();

    useSeoMeta({
      title: i18n.t("recipe-finder.recipe-finder"),
    });

    const useMobile = computed(() => display.smAndDown.value);

    const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || "");
    const { isOwnGroup } = useLoggedInState();
    const api = isOwnGroup.value ? useUserApi() : usePublicExploreApi(groupSlug.value).explore;

    const preferences = useRecipeFinderPreferences();
    const state = reactive({
      ready: false,
      loading: false,
      recipesReady: false,
      settingsMenu: false,
      queryFilterMenu: false,
      queryFilterMenuKey: 0,
      queryFilterEditorValue: "",
      queryFilterEditorValueJSON: {},
      queryFilterJSON: preferences.value.queryFilterJSON,
      settings: {
        maxMissingFoods: preferences.value.maxMissingFoods,
        maxMissingTools: preferences.value.maxMissingTools,
        includeFoodsOnHand: preferences.value.includeFoodsOnHand,
        includeToolsOnHand: preferences.value.includeToolsOnHand,
        queryFilter: preferences.value.queryFilter,
        limit: 20,
      },
    });

    onMounted(() => {
      if (!isOwnGroup.value) {
        state.settings.includeFoodsOnHand = false;
        state.settings.includeToolsOnHand = false;
      }
    });

    watch(
      () => state,
      (newState) => {
        preferences.value.queryFilter = newState.settings.queryFilter;
        preferences.value.queryFilterJSON = newState.queryFilterJSON;
        preferences.value.maxMissingFoods = newState.settings.maxMissingFoods;
        preferences.value.maxMissingTools = newState.settings.maxMissingTools;
        preferences.value.includeFoodsOnHand = newState.settings.includeFoodsOnHand;
        preferences.value.includeToolsOnHand = newState.settings.includeToolsOnHand;
      },
      {
        deep: true,
      },
    );

    const attrs = computed(() => {
      return {
        title: {
          class: {
            readyToMake: "ma-0 pa-0",
            missingItems: recipeSuggestions.value.readyToMake.length ? "ma-0 pa-0 mt-5" : "ma-0 pa-0",
          },
        },
        searchFilter: {
          colClass: useMobile.value ? "d-flex flex-wrap justify-end" : "d-flex flex-wrap justify-start",
          filterClass: useMobile.value ? "ml-4 mb-2" : "mr-4 mb-2",
        },
        settings: {
          colClass: useMobile.value ? "d-flex flex-wrap justify-end" : "d-flex flex-wrap justify-start",
        },
      };
    });

    const foodStore = isOwnGroup.value ? useFoodStore() : usePublicFoodStore(groupSlug.value);
    const selectedFoods = ref<IngredientFood[]>([]);
    function addFood(food: IngredientFood) {
      selectedFoods.value = [...selectedFoods.value, food];
      handleFoodUpdates();
    }
    function removeFood(food: IngredientFood) {
      selectedFoods.value = selectedFoods.value.filter(f => f.id !== food.id);
      handleFoodUpdates();
    }
    function handleFoodUpdates() {
      selectedFoods.value.sort((a, b) => (a.pluralName || a.name).localeCompare(b.pluralName || b.name));
      preferences.value.foodIds = selectedFoods.value.map(food => food.id);
    }
    watch(
      () => selectedFoods.value,
      () => {
        handleFoodUpdates();
      },
    );

    const toolStore = isOwnGroup.value ? useToolStore() : usePublicToolStore(groupSlug.value);
    const selectedTools = ref<RecipeTool[]>([]);
    function addTool(tool: RecipeTool) {
      selectedTools.value = [...selectedTools.value, tool];
      handleToolUpdates();
    }
    function removeTool(tool: RecipeTool) {
      selectedTools.value = selectedTools.value.filter(t => t.id !== tool.id);
      handleToolUpdates();
    }
    function handleToolUpdates() {
      selectedTools.value.sort((a, b) => a.name.localeCompare(b.name));
      preferences.value.toolIds = selectedTools.value.map(tool => tool.id);
    }
    watch(
      () => selectedTools.value,
      () => {
        handleToolUpdates();
      },
    );

    async function hydrateFoods() {
      if (!preferences.value.foodIds.length) {
        return;
      }
      if (!foodStore.store.value.length) {
        await foodStore.actions.refresh();
      }

      const foods = preferences.value.foodIds
        .map(foodId => foodStore.store.value.find(food => food.id === foodId))
        .filter(food => !!food);

      selectedFoods.value = foods;
    }

    async function hydrateTools() {
      if (!preferences.value.toolIds.length) {
        return;
      }
      if (!toolStore.store.value.length) {
        await toolStore.actions.refresh();
      }

      const tools = preferences.value.toolIds
        .map(toolId => toolStore.store.value.find(tool => tool.id === toolId))
        .filter(tool => !!tool);

      selectedTools.value = tools;
    }

    onMounted(async () => {
      await Promise.all([hydrateFoods(), hydrateTools()]);
      state.ready = true;
      if (!selectedFoods.value.length) {
        state.recipesReady = true;
      };
    });

    const recipeResponseItems = ref<RecipeSuggestionResponseItem[]>([]);
    const recipeSuggestions = computed<RecipeSuggestions>(() => {
      const readyToMake: RecipeSuggestionResponseItem[] = [];
      const missingItems: RecipeSuggestionResponseItem[] = [];
      recipeResponseItems.value.forEach((responseItem) => {
        if (responseItem.missingFoods.length === 0 && responseItem.missingTools.length === 0) {
          readyToMake.push(responseItem);
        }
        else {
          missingItems.push(responseItem);
        };
      });

      return {
        readyToMake,
        missingItems,
      };
    });

    watchDebounced(
      [selectedFoods, selectedTools, state.settings], async () => {
        // don't search for suggestions if no foods are selected
        if (!selectedFoods.value.length) {
          recipeResponseItems.value = [];
          state.recipesReady = true;
          return;
        }

        state.loading = true;
        const { data } = await api.recipes.getSuggestions(
          {
            limit: state.settings.limit,
            queryFilter: state.settings.queryFilter,
            maxMissingFoods: state.settings.maxMissingFoods,
            maxMissingTools: state.settings.maxMissingTools,
            includeFoodsOnHand: state.settings.includeFoodsOnHand,
            includeToolsOnHand: state.settings.includeToolsOnHand,
          } as RecipeSuggestionQuery,
          selectedFoods.value.map(food => food.id),
          selectedTools.value.map(tool => tool.id),
        );
        state.loading = false;
        if (!data) {
          return;
        }
        recipeResponseItems.value = data.items;
        state.recipesReady = true;
      },
      {
        debounce: 500,
      },
    );

    const queryFilterBuilderFields: FieldDefinition[] = [
      {
        name: "recipe_category.id",
        label: i18n.t("category.categories"),
        type: Organizer.Category,
      },
      {
        name: "tags.id",
        label: i18n.t("tag.tags"),
        type: Organizer.Tag,
      },
      {
        name: "household_id",
        label: i18n.t("household.households"),
        type: Organizer.Household,
      },
      {
        name: "user_id",
        label: i18n.t("user.users"),
        type: Organizer.User,
      },
    ];

    function clearQueryFilter() {
      state.queryFilterEditorValue = "";
      state.queryFilterEditorValueJSON = { parts: [] } as QueryFilterJSON;
      state.settings.queryFilter = "";
      state.queryFilterJSON = { parts: [] } as QueryFilterJSON;
      state.queryFilterMenu = false;
      state.queryFilterMenuKey += 1;
    }

    function saveQueryFilter() {
      state.settings.queryFilter = state.queryFilterEditorValue || "";
      state.queryFilterJSON = state.queryFilterEditorValueJSON || { parts: [] } as QueryFilterJSON;
      state.queryFilterMenu = false;
    }

    return {
      ...toRefs(state),
      useMobile,
      attrs,
      isOwnGroup,
      foods: foodStore.store,
      selectedFoods,
      addFood,
      removeFood,
      tools: toolStore.store,
      selectedTools,
      addTool,
      removeTool,
      recipeSuggestions,
      queryFilterBuilderFields,
      clearQueryFilter,
      saveQueryFilter,
    };
  },
});
</script>
