<script setup lang="ts">
import {
  TransactionApiClient,
  type RecurringTransactionPayload,
  type RecurringTransactionResponse,
} from '@/api/transaction'
import ModalWrapper from './common/ModalWrapper.vue'
import { computed, ref, useTemplateRef, watch, type Ref } from 'vue'
import { useRoute } from 'vue-router'
import AppForm from './common/form/AppForm.vue'
import FormGroup from './common/form/FormGroup.vue'
import FormControl from './common/form/FormControl.vue'
import AppButton from './common/AppButton.vue'
import { getSelectedProfile } from '@/api/profile.ts'
import { CronExpressionParser } from 'cron-parser'

const route = useRoute()
const props = defineProps<{ draftId: number }>()
const modal = useTemplateRef('modal')
const savedTxn: Ref<RecurringTransactionResponse | undefined> = ref()

// Form Data:
enum ScheduleType {
  Monthly,
  Custom,
}

const chosenScheduleType: Ref<ScheduleType> = ref(ScheduleType.Monthly)
const inputIsValid = ref(true)
const dayOfMonth: Ref<number> = ref(1)
const customScheduleText = ref('')
const customCronErrorMsg = ref<string | null>(null)

// Reset form data when the user chooses a different schedule type.
watch(chosenScheduleType, () => {
  dayOfMonth.value = 1
  customScheduleText.value = ''
  customCronErrorMsg.value = null
})

watch([chosenScheduleType, dayOfMonth, customScheduleText], () => {
  inputIsValid.value = checkInputValid()
})

const upcomingDates = computed(() => {
  if (!inputIsValid.value) return []
  const cronExpr = getScheduleExpression()
  const cron = CronExpressionParser.parse(cronExpr)
  return cron.take(3).map((d) => d.toString())
})

async function show(): Promise<RecurringTransactionResponse | undefined> {
  if (!modal.value) return undefined
  savedTxn.value = undefined
  const result = await modal.value.show()
  if (result === 'saved') {
    return savedTxn.value
  }
  return undefined
}

async function addRecurringTransaction() {
  const payload: RecurringTransactionPayload = {
    draftId: props.draftId,
    scheduleExpr: getScheduleExpression(),
  }
  const api = new TransactionApiClient(getSelectedProfile(route))
  try {
    savedTxn.value = await api.createRecurringTransaction(payload)
    modal.value?.close('saved')
  } catch (err) {
    console.error(err)
    savedTxn.value = undefined
  }
}

function getScheduleExpression(): string {
  if (chosenScheduleType.value === ScheduleType.Custom) {
    try {
      CronExpressionParser.parse(customScheduleText.value)
      return customScheduleText.value.trim()
    } catch (err) {
      console.warn('Failed to parse schedule Cron expression.', err)
      throw err
    }
  } else if (chosenScheduleType.value === ScheduleType.Monthly) {
    return `0 0 12 ${dayOfMonth.value} * *`
  }
  throw new Error('Unsupported schedule type.')
}

function checkInputValid(): boolean {
  if (chosenScheduleType.value === ScheduleType.Custom) {
    customCronErrorMsg.value = null
    if (customScheduleText.value.trim().length === 0) {
      customCronErrorMsg.value = 'Missing cron expression.'
      return false
    }
    try {
      const expr = CronExpressionParser.parse(customScheduleText.value)
      if (expr.hasNext()) {
        return true
      } else {
        customCronErrorMsg.value = 'This cron expression has no discernable next date.'
        return false
      }
    } catch (err) {
      if (err && typeof err === 'object' && 'message' in err && typeof err.message === 'string') {
        customCronErrorMsg.value = err.message
      }
      return false
    }
  } else if (chosenScheduleType.value === ScheduleType.Monthly) {
    return dayOfMonth.value >= 1 && dayOfMonth.value <= 28
  }
  return false
}

defineExpose({ show })
</script>
<template>
  <ModalWrapper ref="modal">
    <template v-slot:default>
      <h2>Add Recurring Transaction</h2>
      <p>
        Use this template to add a <em>recurring transaction</em>. According to the interval you
        specify below, Finnow will create a new draft using this template each time, which you can
        then review and submit.
      </p>
      <p>
        Each time a new draft is created from this template, the new draft's timestamp will be set
        to the current date and time.
      </p>
      <p class="font-italic font-size-small text-muted">
        Note that Finnow processes recurring transactions at a regular interval, but to avoid
        issues, please don't create recurring transactions whose schedule is less than hourly, as
        these may be too frequent for the system to process reliably without skipping.
      </p>
      <AppForm>
        <FormGroup>
          <FormControl label="Type of Schedule">
            <select v-model="chosenScheduleType">
              <option :value="ScheduleType.Monthly">Monthly</option>
              <option :value="ScheduleType.Custom">Custom</option>
            </select>
          </FormControl>
        </FormGroup>

        <div v-if="chosenScheduleType === ScheduleType.Monthly">
          <h3>Monthly Schedule</h3>
          <p>
            Schedule a recurring transaction to occur on the same calendar day every month. To
            ensure that this schedule works all year, you must pick a day between the 1st and 28th.
          </p>
          <FormGroup>
            <FormControl
              label="Day of Month"
              hint="Specify on which day of the month this transaction occurs."
            >
              <input
                type="number"
                step="1"
                min="1"
                max="28"
                v-model="dayOfMonth"
                style="max-width: 100px"
              />
            </FormControl>
          </FormGroup>
        </div>

        <div v-if="chosenScheduleType === ScheduleType.Custom">
          <h3>Custom Schedule</h3>
          <p>
            Use a custom
            <a
              href="https://en.wikipedia.org/wiki/Cron"
              target="_blank"
              >Cron</a
            >
            expression to define when this recurring transaction should happen.
          </p>
          <FormGroup>
            <FormControl label="Schedule Expression">
              <input
                type="text"
                maxlength="64"
                v-model="customScheduleText"
                style="max-width: 300px"
              />
            </FormControl>
          </FormGroup>
          <p
            v-if="customCronErrorMsg"
            class="text-negative font-size-xsmall m0"
          >
            Failed to parse cron expression: {{ customCronErrorMsg }}
          </p>
          <p
            v-if="customCronErrorMsg === null"
            class="text-positive font-size-xsmall m0"
          >
            Cron expression is valid.
          </p>
        </div>

        <!-- A little area at the bottom to show hypothetical upcoming dates with the chosen schedule: -->
        <div v-if="inputIsValid">
          <p class="font-size-small mb-0 font-italic">
            Using the schedule you've chosen above, here are some upcoming dates at which this
            recurring transaction will create a new draft.
          </p>
          <ul class="font-size-small mt-1">
            <li
              v-for="d in upcomingDates"
              :key="d"
            >
              {{ d }}
            </li>
          </ul>
        </div>
      </AppForm>
    </template>
    <template v-slot:buttons>
      <AppButton
        :disabled="!inputIsValid"
        @click="addRecurringTransaction()"
        >Add</AppButton
      >
      <AppButton
        button-style="secondary"
        @click="modal?.close()"
        >Cancel</AppButton
      >
    </template>
  </ModalWrapper>
</template>
