/*
This file is part of the Notesnook project (https://notesnook.com/)

Copyright (C) 2023 Streetwriters (Private) Limited

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import { Note, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import dayjs from "dayjs";
import React, { useRef, useState } from "react";
import {
  KeyboardAvoidingView,
  Platform,
  ScrollView,
  TextInput,
  View
} from "react-native";
import DatePicker from "react-native-date-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../../common/database";
import { Dialog } from "../../components/dialog";
import { Header } from "../../components/header";
import { Button } from "../../components/ui/button";
import { ReminderTime } from "../../components/ui/reminder-time";
import Paragraph from "../../components/ui/typography/paragraph";
import { DDS } from "../../services/device-detection";
import { eSendEvent, ToastManager } from "../../services/event-manager";
import Navigation, { NavigationProps } from "../../services/navigation";
import Notifications from "../../services/notifications";
import SettingsService from "../../services/settings";
import { useRelationStore } from "../../stores/use-relation-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import {
  getFormattedDate,
  useIsFeatureAvailable,
  usePromise
} from "@notesnook/common";
import PaywallSheet from "../../components/sheets/paywall";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import { Pressable } from "../../components/ui/pressable";
import { TimeSince } from "../../components/ui/time-since";
import Heading from "../../components/ui/typography/heading";
import { eOnLoadNote } from "../../utils/events";
import { fluidTabsRef } from "../../utils/global-refs";
import FormInput, {
  createFormRef,
  validators
} from "../../components/ui/input/form-input";
import AppIcon from "../../components/ui/AppIcon";

const ReminderModes =
  Platform.OS === "ios"
    ? {
        Once: "once",
        Repeat: "repeat"
      }
    : {
        Once: "once",
        Repeat: "repeat",
        Permanent: "permanent"
      };

const RecurringModes = {
  Daily: "day",
  Week: "week",
  Month: "month",
  Year: "year"
};

const WeekDays = [0, 1, 2, 3, 4, 5, 6];
const WeekDaysMon = [1, 2, 3, 4, 5, 6, 0];
const MonthDays = new Array(31).fill(true);

const ReminderNotificationModes = {
  Silent: "silent",
  Vibrate: "vibrate",
  Urgent: "urgent"
};

export default function AddReminder(props: NavigationProps<"AddReminder">) {
  const { reminder, reference } = props.route.params;
  useNavigationFocus(props.navigation, {
    focusOnInit: true,
    onFocus: () => {
      if (!props.route.params?.reminder) {
        setTimeout(() => {
          titleRef.current?.focus();
        }, 200);
      }
      return false;
    }
  });
  const { colors, isDark } = useThemeColors();
  const weekFormat = useSettingStore((state) => state.weekFormat);
  const [reminderMode, setReminderMode] = useState<Reminder["mode"]>(
    reminder?.mode || "once"
  );
  const [recurringMode, setRecurringMode] = useState<Reminder["recurringMode"]>(
    reminder?.recurringMode || "week"
  );
  const [selectedDays, setSelectedDays] = useState<number[]>(
    reminder?.selectedDays || []
  );
  const [date, setDate] = useState<Date>(
    new Date(reminder?.date || Date.now())
  );
  const [reminderNotificationMode, setReminderNotificatioMode] = useState<
    Reminder["priority"]
  >(reminder?.priority || SettingsService.get().reminderNotificationMode);
  const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
  const [repeatFrequency, setRepeatFrequency] = useState(1);
  const referencedItem = reference ? (reference as Note) : null;
  const recurringReminderFeature = useIsFeatureAvailable("recurringReminders");
  const formRef = useRef(
    createFormRef({
      title: reminder?.title || referencedItem?.title || "",
      description: reminder?.description || referencedItem?.headline || ""
    })
  );
  const title = useRef<string | undefined>(
    !reminder ? referencedItem?.title : reminder?.title
  );
  const details = useRef<string | undefined>(
    !reminder ? referencedItem?.headline : reminder?.description
  );
  const titleRef = useRef<TextInput>(null);
  const descriptionRef = useRef<TextInput>(null);
  const timer = useRef<NodeJS.Timeout>(undefined);
  const referencedNotes = usePromise(
    () =>
      reminder?.id
        ? db.relations
            .to({ id: reminder.id, type: "reminder" }, "note")
            .resolve()
        : null,
    [reminder?.id]
  );
  const [dateError, setDateError] = useState<string>();
  const [selectDayError, setSelectDayError] = useState<string>();

  const showDatePicker = () => {
    setDatePickerVisibility(true);
  };

  const hideDatePicker = () => {
    setDatePickerVisibility(false);
  };

  const handleConfirm = (date: Date) => {
    timer.current = setTimeout(() => {
      setDateError(undefined);
      hideDatePicker();
      setDate(date);
    }, 10);
  };
  function nth(n: number) {
    return (
      ["st", "nd", "rd"][(((((n < 0 ? -n : n) + 90) % 100) - 10) % 10) - 1] ||
      "th"
    );
  }

  function getSelectedDaysText(selectedDays: number[]) {
    const text = selectedDays
      .sort((a, b) => a - b)
      .map((day, index) => {
        const isLast = index === selectedDays.length - 1;
        const isSecondLast = index === selectedDays.length - 2;
        const joinWith = isSecondLast ? " & " : isLast ? "" : ", ";
        return recurringMode === RecurringModes.Week
          ? strings.weekDayNames[day as keyof typeof strings.weekDayNames]() +
              joinWith
          : `${day}${nth(day)} ${joinWith}`;
      })
      .join("");
    return text;
  }

  async function saveReminder() {
    try {
      if (!formRef.current.validate()) return;
      if (date.getTime() < Date.now() && reminderMode === "once") {
        setDateError(strings.dateError());
        return;
      }

      if (
        reminderMode === ReminderModes.Repeat &&
        recurringMode !== "day" &&
        recurringMode !== "year" &&
        selectedDays.length === 0
      ) {
        setSelectDayError(strings.selectDayError());
        return;
      }

      if (!date && reminderMode !== ReminderModes.Permanent) return;

      if (!(await Notifications.checkAndRequestPermissions(true)))
        throw new Error(strings.noNotificationPermission());

      date.setSeconds(0, 0);

      const reminderId = await db.reminders?.add({
        id: reminder?.id,
        date: date?.getTime(),
        priority: reminderNotificationMode,
        title: title.current,
        description: details.current,
        recurringMode: recurringMode,
        selectedDays: selectedDays,
        mode: reminderMode,
        localOnly: reminderMode === "permanent",
        snoozeUntil:
          date?.getTime() > Date.now() ? undefined : reminder?.snoozeUntil,
        disabled: false
      });
      if (!reminderId) return;
      const _reminder = await db.reminders?.reminder(reminderId);

      if (reference && _reminder) {
        await db.relations?.add(reference, {
          id: _reminder?.id as string,
          type: _reminder?.type
        });
      }
      Notifications.scheduleNotification(_reminder as Reminder);
      Navigation.queueRoutesForUpdate();
      useRelationStore.getState().update();
      Navigation.goBack();
    } catch (e) {
      ToastManager.error(e as Error, undefined);
    }
  }

  const KeyboardViewIOS = Platform.OS === "ios" ? KeyboardAvoidingView : View;

  return (
    <SafeAreaView
      style={{
        backgroundColor: colors.primary.background,
        flex: 1
      }}
    >
      <KeyboardViewIOS
        behavior="padding"
        style={{
          flex: 1
        }}
      >
        <Header
          title={reminder ? strings.editReminder() : strings.newReminder()}
          canGoBack
          rightButton={{
            name: "check",
            onPress: saveReminder
          }}
        />
        <Dialog context="local" />
        <ScrollView
          style={{
            marginBottom: DDS.isTab ? 25 : undefined,
            paddingHorizontal: DefaultAppStyles.GAP
          }}
          contentContainerStyle={{
            gap: DefaultAppStyles.GAP_VERTICAL
          }}
          keyboardDismissMode="interactive"
          keyboardShouldPersistTaps="handled"
        >
          <FormInput
            name="title"
            validators={[validators.required(strings.titleIsRequired())]}
            formRef={formRef}
            fwdRef={titleRef}
            defaultValue={reminder?.title || referencedItem?.title}
            placeholder={strings.remindeMeOf()}
            onChangeText={(text) => (title.current = text)}
            wrapperStyle={{
              marginTop: DefaultAppStyles.GAP_VERTICAL
            }}
            onSubmitEditing={() => {
              descriptionRef.current?.focus();
            }}
          />

          <FormInput
            name="description"
            validators={[]}
            formRef={formRef}
            defaultValue={
              reminder ? reminder?.description : referencedItem?.headline
            }
            fwdRef={descriptionRef}
            placeholder={strings.addShortNote()}
            onChangeText={(text) => (details.current = text)}
            containerStyle={{
              maxHeight: 80
            }}
            multiline
            textAlignVertical="top"
            inputStyle={{
              minHeight: 80,
              paddingVertical: DefaultAppStyles.GAP_VERTICAL
            }}
            height={80}
          />

          <ScrollView
            style={{
              flexDirection: "row"
            }}
            horizontal
          >
            {Object.keys(ReminderModes).map((mode) => (
              <Button
                key={mode}
                title={strings.reminderModes(
                  ReminderModes[mode as keyof typeof ReminderModes] as string
                )}
                style={{
                  paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
                  marginRight: DefaultAppStyles.GAP_SMALL
                }}
                proTag={mode === "Repeat"}
                height={35}
                type={
                  reminderMode ===
                  ReminderModes[mode as keyof typeof ReminderModes]
                    ? "selectedAccent"
                    : "plain"
                }
                onPress={() => {
                  if (
                    recurringReminderFeature &&
                    !recurringReminderFeature?.isAllowed
                  ) {
                    PaywallSheet.present(recurringReminderFeature);
                    return;
                  }

                  setReminderMode(
                    ReminderModes[
                      mode as keyof typeof ReminderModes
                    ] as Reminder["mode"]
                  );
                  if (mode === "Repeat") {
                    setSelectedDays((days) => {
                      if (days.length > 0) return days;
                      if (days.indexOf(date.getDay()) > -1) {
                        return days;
                      }
                      days.push(date.getDay());
                      return [...days];
                    });
                  }
                }}
              />
            ))}
          </ScrollView>

          {reminderMode === ReminderModes.Repeat ? (
            <View
              style={{
                backgroundColor: colors.secondary.background,
                padding: DefaultAppStyles.GAP,
                borderRadius: defaultBorderRadius
              }}
            >
              <View
                style={{
                  flexDirection: "row",
                  marginBottom:
                    recurringMode === "day" || recurringMode === "year"
                      ? 0
                      : 12,
                  alignItems: "center"
                }}
              >
                {Object.keys(RecurringModes).map((mode) => (
                  <Button
                    key={mode}
                    title={strings.recurringModes(
                      RecurringModes[mode as keyof typeof RecurringModes]
                    )}
                    style={{
                      marginRight: 6,
                      borderRadius: 100,
                      paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
                    }}
                    type={
                      recurringMode ===
                      RecurringModes[mode as keyof typeof RecurringModes]
                        ? "selected"
                        : "plain"
                    }
                    onPress={() => {
                      setRecurringMode(
                        RecurringModes[
                          mode as keyof typeof RecurringModes
                        ] as Reminder["recurringMode"]
                      );
                      setSelectedDays([]);
                      setRepeatFrequency(1);
                    }}
                  />
                ))}
              </View>

              <ScrollView showsHorizontalScrollIndicator={false} horizontal>
                {recurringMode === RecurringModes.Daily ||
                recurringMode === RecurringModes.Year
                  ? null
                  : recurringMode === RecurringModes.Week
                    ? (weekFormat === "Mon" ? WeekDaysMon : WeekDays).map(
                        (item) => (
                          <Button
                            key={strings.weekDayNamesShort[
                              item as keyof typeof strings.weekDayNamesShort
                            ]()}
                            title={strings.weekDayNamesShort[
                              item as keyof typeof strings.weekDayNamesShort
                            ]()}
                            type={
                              selectedDays.indexOf(item) > -1
                                ? "selected"
                                : "plain"
                            }
                            fontSize={AppFontSize.xs}
                            style={{
                              height: 40,
                              borderRadius: 100,
                              marginRight: 10
                            }}
                            onPress={() => {
                              setSelectedDays((days) => {
                                if (days.indexOf(item) > -1) {
                                  days.splice(days.indexOf(item), 1);
                                  return [...days];
                                }
                                days.push(item);
                                return [...days];
                              });
                            }}
                          />
                        )
                      )
                    : MonthDays.map((item, index) => (
                        <Button
                          key={index + "monthday"}
                          title={index + 1 + ""}
                          type={
                            selectedDays.indexOf(index + 1) > -1
                              ? "selected"
                              : "plain"
                          }
                          fontSize={AppFontSize.xs}
                          style={{
                            height: 40,
                            borderRadius: 100,
                            marginRight: 10
                          }}
                          onPress={() => {
                            setSelectedDays((days) => {
                              if (days.indexOf(index + 1) > -1) {
                                days.splice(days.indexOf(index + 1), 1);
                                return [...days];
                              }
                              days.push(index + 1);
                              return [...days];
                            });
                          }}
                        />
                      ))}
              </ScrollView>
              {selectDayError ? (
                <Paragraph
                  size={AppFontSize.xs}
                  style={{
                    marginTop: DefaultAppStyles.GAP_VERTICAL,
                    color: colors.error.icon
                  }}
                >
                  <AppIcon
                    color={colors.error.accent}
                    name="alert-circle-outline"
                    size={AppFontSize.sm - 1}
                  />{" "}
                  {selectDayError}
                </Paragraph>
              ) : null}
            </View>
          ) : null}

          {reminderMode === ReminderModes.Permanent ? null : (
            <View
              style={{
                width: "100%",
                flexDirection: "column",
                justifyContent: "center",
                alignItems: "center"
              }}
            >
              <DateTimePickerModal
                isVisible={isDatePickerVisible}
                mode="datetime"
                minimumDate={
                  reminderMode === "once" ? dayjs().toDate() : new Date(0)
                }
                onConfirm={handleConfirm}
                onCancel={hideDatePicker}
                isDarkModeEnabled={isDark}
                firstDayOfWeek={weekFormat === "Mon" ? 1 : 0}
                is24Hour={db.settings.getTimeFormat() === "24-hour"}
                date={date || new Date(Date.now())}
                themeVariant={isDark ? "dark" : "light"}
              />

              <DatePicker
                date={date}
                minimumDate={
                  reminderMode === "once" ? dayjs().toDate() : new Date(0)
                }
                maximumDate={dayjs(date).add(3, "months").toDate()}
                onDateChange={handleConfirm}
                theme={isDark ? "dark" : "light"}
                is24hourSource="locale"
                locale={
                  db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
                }
                mode={
                  reminderMode === ReminderModes.Repeat &&
                  recurringMode !== "year"
                    ? "time"
                    : "datetime"
                }
              />

              {reminderMode === ReminderModes.Repeat ? null : (
                <Button
                  style={{
                    width: "100%"
                  }}
                  title={
                    date
                      ? getFormattedDate(date, "date-time")
                      : strings.selectDate()
                  }
                  type={date ? "secondaryAccented" : "secondary"}
                  icon="calendar"
                  fontSize={AppFontSize.sm}
                  onPress={() => {
                    showDatePicker();
                  }}
                />
              )}

              {dateError ? (
                <Paragraph
                  size={AppFontSize.xs}
                  style={{
                    marginTop: DefaultAppStyles.GAP_VERTICAL,
                    color: colors.error.icon
                  }}
                >
                  <AppIcon
                    color={colors.error.accent}
                    name="alert-circle-outline"
                    size={AppFontSize.sm - 1}
                  />{" "}
                  {dateError}
                </Paragraph>
              ) : null}
            </View>
          )}

          {reminderMode === ReminderModes.Once ||
          reminderMode === ReminderModes.Permanent ? null : (
            <View
              style={{
                borderRadius: defaultBorderRadius,
                flexDirection: "row",
                alignItems: "center",
                justifyContent: "flex-start"
              }}
            >
              <>
                <Paragraph
                  size={AppFontSize.xxs}
                  color={colors.secondary.paragraph}
                >
                  {recurringMode === RecurringModes.Daily
                    ? strings.reminderRepeatStrings.day(
                        dayjs(date).format("hh:mm A")
                      )
                    : recurringMode === RecurringModes.Year
                      ? strings.reminderRepeatStrings.year(
                          dayjs(date).format("dddd, MMMM D, h:mm A")
                        )
                      : selectedDays.length === 7 &&
                          recurringMode === RecurringModes.Week
                        ? strings.reminderRepeatStrings.week.daily(
                            dayjs(date).format("hh:mm A")
                          )
                        : selectedDays.length === 0
                          ? strings.reminderRepeatStrings[
                              recurringMode as "week" | "month"
                            ].selectDays()
                          : strings.reminderRepeatStrings.repeats(
                              repeatFrequency,
                              recurringMode as string,
                              getSelectedDaysText(selectedDays),
                              dayjs(date).format("hh:mm A")
                            )}
                </Paragraph>
              </>
            </View>
          )}

          {reminderMode === ReminderModes.Permanent ? null : (
            <ScrollView
              style={{
                flexDirection: "row",
                height: 50
              }}
              horizontal
            >
              {Object.keys(ReminderNotificationModes).map((mode) => (
                <Button
                  key={mode}
                  title={strings.reminderNotificationModes(
                    mode as keyof typeof ReminderNotificationModes
                  )}
                  style={{
                    marginRight: 12,
                    paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
                  }}
                  icon={
                    mode === "Silent"
                      ? "minus-circle"
                      : mode === "Vibrate"
                        ? "vibrate"
                        : "volume-high"
                  }
                  fontSize={AppFontSize.xs}
                  height={35}
                  type={
                    reminderNotificationMode ===
                    ReminderNotificationModes[
                      mode as keyof typeof ReminderNotificationModes
                    ]
                      ? "selectedAccent"
                      : "plain"
                  }
                  onPress={() => {
                    const _mode = ReminderNotificationModes[
                      mode as keyof typeof ReminderNotificationModes
                    ] as Reminder["priority"];
                    SettingsService.set({
                      reminderNotificationMode: _mode
                    });
                    setReminderNotificatioMode(_mode);
                  }}
                />
              ))}
            </ScrollView>
          )}

          <ReminderTime
            reminder={reminder}
            style={{
              width: "100%",
              justifyContent: "flex-start",
              paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
              alignSelf: "flex-start"
            }}
          />

          {referencedNotes &&
          referencedNotes.status === "fulfilled" &&
          referencedNotes.value !== null &&
          referencedNotes.value?.length > 0 ? (
            <View
              style={{
                gap: DefaultAppStyles.GAP_VERTICAL
              }}
            >
              <Heading size={AppFontSize.md}>{strings.referencedIn()}</Heading>
              {referencedNotes.value.map((item) => (
                <Pressable
                  key={item.id}
                  style={{
                    justifyContent: "space-between",
                    flexDirection: "row",
                    paddingHorizontal: DefaultAppStyles.GAP,
                    paddingVertical: DefaultAppStyles.GAP_VERTICAL
                  }}
                  onPress={() => {
                    Navigation.navigate("FluidPanelsView");
                    fluidTabsRef.current?.goToPage("editor");
                    eSendEvent(eOnLoadNote, {
                      item: item
                    });
                  }}
                  type="secondary"
                >
                  <Paragraph>{item.title}</Paragraph>
                  <TimeSince
                    style={{
                      fontSize: AppFontSize.xxs,
                      color: colors.secondary.paragraph,
                      marginRight: 6
                    }}
                    time={item.dateEdited}
                    updateFrequency={
                      Date.now() - item.dateEdited < 60000 ? 2000 : 60000
                    }
                  />
                </Pressable>
              ))}
            </View>
          ) : null}
        </ScrollView>
      </KeyboardViewIOS>
    </SafeAreaView>
  );
}

AddReminder.present = (reminder?: Reminder, reference?: Note) => {
  Navigation.navigate("AddReminder", {
    reminder,
    reference
  });
};
