import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { CalendarDate, getLocalTimeZone, today } from '@internationalized/date';
import type { DateValue } from '@internationalized/date';
import { ref } from 'vue';
import UiCalendar from './UiCalendar.vue';
import { UiIcon } from '../UiIcon';

/**
 * UiCalendar is a month-grid date picker for selecting a single calendar day.
 * Use it standalone or inside a `UiPopover` to power a date input — see the `UiDatePicker` pattern for the composed flow.
 * For range selection, use `UiRangeCalendar`.
 */
const meta: Meta<typeof UiCalendar> = {
  title: 'Components/UiCalendar',
  component: UiCalendar,
  tags: ['autodocs'],
  decorators: [
    () => ({
      template: '<div class="inline-block"><story /></div>',
    }),
  ],
  argTypes: {
    disabled: {
      control: 'boolean',
      description: 'Whether the calendar is disabled.',
    },
    readonly: {
      control: 'boolean',
      description:
        'Whether the calendar is read-only. Users can navigate but not select dates.',
    },
    locale: {
      control: 'select',
      options: ['en', 'de', 'fr', 'es', 'pl'],
      description: 'The locale to use for formatting dates and weekday names.',
    },
    weekStartsOn: {
      control: 'select',
      options: [0, 1, 2, 3, 4, 5, 6],
      description:
        'The day of the week to start on. 0 is the locale default first day.',
    },
    numberOfMonths: {
      control: { type: 'number', min: 1, max: 3 },
      description: 'The number of months to display at once.',
    },
    pagedNavigation: {
      control: 'boolean',
      description:
        'Whether to navigate by page (all visible months) or by single month.',
    },
    weekdayFormat: {
      control: 'select',
      options: ['narrow', 'short', 'long'],
      description:
        'The format to use for weekday labels. Note: "long" is not recommended as full weekday names do not fit well.',
    },
    fixedWeeks: {
      control: 'boolean',
      description: 'Whether to always show 6 weeks in the calendar.',
    },
    layout: {
      control: 'select',
      options: [undefined, 'month-and-year', 'month-only', 'year-only'],
      description: 'Layout mode for the calendar header dropdowns.',
    },
    preventDeselect: {
      control: 'boolean',
      description: 'Whether to prevent deselecting a date.',
    },
    multiple: {
      control: 'boolean',
      description: 'Whether multiple dates can be selected.',
    },
    calendarLabel: {
      control: 'text',
      description: 'The accessible label for the calendar.',
    },
    initialFocus: {
      control: 'boolean',
      description: 'If true, focuses the calendar on mount.',
    },
    dir: {
      control: 'select',
      options: ['ltr', 'rtl'],
      description: 'The reading direction of the calendar.',
    },
    disableDaysOutsideCurrentView: {
      control: 'boolean',
      description: 'Whether to disable days outside the current view.',
    },
  },
  args: {
    disabled: false,
    readonly: false,
    locale: 'en',
    weekStartsOn: 0,
    numberOfMonths: 1,
    pagedNavigation: false,
    weekdayFormat: 'narrow',
    fixedWeeks: false,
    layout: undefined,
    preventDeselect: false,
    multiple: false,
    initialFocus: false,
    dir: 'ltr',
    disableDaysOutsideCurrentView: false,
  },
};

export default meta;

type Story = StoryObj<typeof UiCalendar>;

const defaultTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { ref } from 'vue'
import { UiCalendar } from '@aleph-alpha/ui-library'

const date = ref<DateValue>()
</script>

<template>
  <UiCalendar
    v-model="date"
  />
</template>`;

/**
 * Basic calendar with date selection.
 * Use the Controls panel to explore all available props.
 */
export const Default: Story = {
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));
      return { date, placeholder, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :disabled="args.disabled"
      :readonly="args.readonly"
      :locale="args.locale"
      :week-starts-on="args.weekStartsOn"
      :number-of-months="args.numberOfMonths"
      :paged-navigation="args.pagedNavigation"
      :weekday-format="args.weekdayFormat"
      :fixed-weeks="args.fixedWeeks"
      :layout="args.layout"
      :prevent-deselect="args.preventDeselect"
      :multiple="args.multiple"
      :calendar-label="args.calendarLabel"
      :initial-focus="args.initialFocus"
      :dir="args.dir"
      :disable-days-outside-current-view="args.disableDaysOutsideCurrentView"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: defaultTemplateSource,
      },
    },
  },
};

const withLayoutTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { CalendarDate, UiCalendar } from '@aleph-alpha/ui-library'
import { ref } from 'vue'

const date = ref<DateValue>()
const minValue = new CalendarDate(1925, 1, 1)
const maxValue = new CalendarDate(2035, 1, 1)
</script>

<template>
  <UiCalendar
    v-model="date"
    layout="month-and-year"
    :min-value="minValue"
    :max-value="maxValue"
  />
</template>`;

/**
 * Calendar with month and year dropdown selectors for quick navigation.
 * Useful for date-of-birth pickers or when users need to navigate far from the current date.
 */
export const WithMonthYearDropdowns: Story = {
  args: {
    layout: 'month-and-year',
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const minValue = new CalendarDate(1925, 1, 1);
      const maxValue = new CalendarDate(2035, 1, 1);
      return { date, minValue, maxValue, args };
    },
    template: `<UiCalendar
      v-model="date"
      :layout="args.layout"
      :min-value="minValue"
      :max-value="maxValue"
      :disabled="args.disabled"
      :readonly="args.readonly"
      :locale="args.locale"
      :week-starts-on="args.weekStartsOn"
      :weekday-format="args.weekdayFormat"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: withLayoutTemplateSource,
      },
    },
  },
};

const multipleMonthsTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { ref } from 'vue'
import { UiCalendar } from '@aleph-alpha/ui-library'

const date = ref<DateValue>()
</script>

<template>
  <UiCalendar
    v-model="date"
    :number-of-months="2"
  />
</template>`;

/**
 * Calendar displaying multiple months side by side.
 * Useful for date range selection or when context of adjacent months is helpful.
 */
export const MultipleMonths: Story = {
  args: {
    numberOfMonths: 2,
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));
      return { date, placeholder, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :number-of-months="args.numberOfMonths"
      :paged-navigation="args.pagedNavigation"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: multipleMonthsTemplateSource,
      },
    },
  },
};

const disabledDatesTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { getLocalTimeZone, UiCalendar } from '@aleph-alpha/ui-library'
import { ref } from 'vue'

const date = ref<DateValue>()

// Disable weekends (Sunday = 0, Saturday = 6)
const isDateDisabled = (date: DateValue) => {
  const dayOfWeek = date.toDate(getLocalTimeZone()).getDay()
  return dayOfWeek === 0 || dayOfWeek === 6
}
</script>

<template>
  <UiCalendar
    v-model="date"
    :is-date-disabled="isDateDisabled"
  />
</template>`;

/**
 * Calendar with weekends disabled using the `isDateDisabled` function.
 * Use this pattern to disable specific dates based on custom logic.
 */
export const DisabledWeekends: Story = {
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));

      // Disable weekends (Sunday = 0, Saturday = 6 in JavaScript's getDay())
      const isDateDisabled = (d: DateValue) => {
        const dayOfWeek = d.toDate(getLocalTimeZone()).getDay();
        return dayOfWeek === 0 || dayOfWeek === 6;
      };

      return { date, placeholder, isDateDisabled, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :is-date-disabled="isDateDisabled"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: disabledDatesTemplateSource,
      },
    },
  },
};

const dateRangeTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { getLocalTimeZone, today, UiCalendar } from '@aleph-alpha/ui-library'
import { ref } from 'vue'

const date = ref<DateValue>()
const minValue = today(getLocalTimeZone())
const maxValue = today(getLocalTimeZone()).add({ months: 3 })
</script>

<template>
  <UiCalendar
    v-model="date"
    :min-value="minValue"
    :max-value="maxValue"
  />
</template>`;

/**
 * Calendar with min and max date constraints.
 * Only dates within the next 3 months are selectable.
 */
export const WithDateRange: Story = {
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const todayDate = today(getLocalTimeZone());
      const minValue = todayDate;
      const maxValue = todayDate.add({ months: 3 });

      return { date, minValue, maxValue, args };
    },
    template: `<UiCalendar
      v-model="date"
      :min-value="minValue"
      :max-value="maxValue"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: dateRangeTemplateSource,
      },
    },
  },
};

const germanLocaleTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { ref } from 'vue'
import { UiCalendar } from '@aleph-alpha/ui-library'

const date = ref<DateValue>()
</script>

<template>
  <UiCalendar
    v-model="date"
    locale="de"
    weekday-format="short"
  />
</template>`;

/**
 * Calendar with German locale and week starting on Monday.
 * Demonstrates i18n capabilities.
 */
export const GermanLocale: Story = {
  args: {
    locale: 'de',
    weekdayFormat: 'short',
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));
      return { date, placeholder, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :locale="args.locale"
      :weekday-format="args.weekdayFormat"
      :disabled="args.disabled"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: germanLocaleTemplateSource,
      },
    },
  },
};

const disabledTemplateSource = `<script setup lang="ts">
import { UiCalendar } from '@aleph-alpha/ui-library'
</script>

<template>
  <UiCalendar disabled />
</template>`;

/**
 * Disabled calendar state.
 * Users cannot interact with the calendar.
 */
export const Disabled: Story = {
  args: {
    disabled: true,
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const placeholder = ref(today(getLocalTimeZone()));
      return { placeholder, args };
    },
    template: `<UiCalendar
      :default-placeholder="placeholder"
      :disabled="args.disabled"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: disabledTemplateSource,
      },
    },
  },
};

const readonlyTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { getLocalTimeZone, today, UiCalendar } from '@aleph-alpha/ui-library'
import { ref } from 'vue'

const date = ref<DateValue>(today(getLocalTimeZone()))
</script>

<template>
  <UiCalendar
    v-model="date"
    readonly
  />
</template>`;

/**
 * Read-only calendar state.
 * Users can navigate between months but cannot select dates.
 */
export const Readonly: Story = {
  args: {
    readonly: true,
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>(today(getLocalTimeZone()));
      return { date, args };
    },
    template: `<UiCalendar
      v-model="date"
      :readonly="args.readonly"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: readonlyTemplateSource,
      },
    },
  },
};

const multipleSelectionTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { ref } from 'vue'
import { UiCalendar } from '@aleph-alpha/ui-library'

const dates = ref<DateValue[]>([])
</script>

<template>
  <UiCalendar
    v-model="dates"
    multiple
  />
</template>`;

/**
 * Calendar with multiple date selection enabled.
 * Users can select multiple dates by clicking on them.
 */
export const MultipleSelection: Story = {
  args: {
    multiple: true,
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const dates = ref<DateValue[]>([]);
      const placeholder = ref(today(getLocalTimeZone()));
      return { dates, placeholder, args };
    },
    template: `<UiCalendar
      v-model="dates"
      :default-placeholder="placeholder"
      :multiple="args.multiple"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: multipleSelectionTemplateSource,
      },
    },
  },
};

const unavailableDatesTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { ref } from 'vue'
import { UiCalendar } from '@aleph-alpha/ui-library'

const date = ref<DateValue>()

// Mark certain dates as unavailable (e.g., holidays)
const isDateUnavailable = (date: DateValue) => {
  return date.day === 15 || date.day === 25
}
</script>

<template>
  <UiCalendar
    v-model="date"
    :is-date-unavailable="isDateUnavailable"
  />
</template>`;

/**
 * Calendar with unavailable dates marked.
 * Unavailable dates are styled differently (strikethrough) but may still be selectable.
 * Use this for holidays or dates with limited availability.
 */
export const UnavailableDates: Story = {
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));

      // Mark 15th and 25th of each month as unavailable
      const isDateUnavailable = (d: DateValue) => {
        return d.day === 15 || d.day === 25;
      };

      return { date, placeholder, isDateUnavailable, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :is-date-unavailable="isDateUnavailable"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: unavailableDatesTemplateSource,
      },
    },
  },
};

const fixedWeeksTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { ref } from 'vue'
import { UiCalendar } from '@aleph-alpha/ui-library'

const date = ref<DateValue>()
</script>

<template>
  <UiCalendar
    v-model="date"
    fixed-weeks
  />
</template>`;

/**
 * Calendar that always shows 6 weeks.
 * Useful for maintaining consistent height regardless of the month.
 */
export const FixedWeeks: Story = {
  args: {
    fixedWeeks: true,
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));
      return { date, placeholder, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :fixed-weeks="args.fixedWeeks"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: fixedWeeksTemplateSource,
      },
    },
  },
};

const preventDeselectTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { getLocalTimeZone, today, UiCalendar } from '@aleph-alpha/ui-library'
import { ref } from 'vue'

const date = ref<DateValue>(today(getLocalTimeZone()))
</script>

<template>
  <UiCalendar
    v-model="date"
    prevent-deselect
  />
</template>`;

/**
 * Calendar that prevents deselecting a date.
 * Once a date is selected, clicking it again won't deselect it.
 * The user must select a different date instead.
 */
export const PreventDeselect: Story = {
  args: {
    preventDeselect: true,
  },
  render: (args) => ({
    components: { UiCalendar },
    setup() {
      const date = ref<DateValue>(today(getLocalTimeZone()));
      return { date, args };
    },
    template: `<UiCalendar
      v-model="date"
      :prevent-deselect="args.preventDeselect"
      :disabled="args.disabled"
      :locale="args.locale"
    />`,
  }),
  parameters: {
    docs: {
      source: {
        code: preventDeselectTemplateSource,
      },
    },
  },
};

const customHeadingTemplateSource = `<script setup lang="ts">
import type { DateValue } from '@aleph-alpha/ui-library'
import { getLocalTimeZone, today, UiCalendar, UiIcon } from '@aleph-alpha/ui-library'
import { ref } from 'vue'

const date = ref<DateValue>()
const defaultPlaceholder = today(getLocalTimeZone())
</script>

<template>
  <UiCalendar
    v-model="date"
    :default-placeholder="defaultPlaceholder"
    weekday-format="short"
    class="rounded-md border shadow-sm **:data-[slot=calendar-cell-trigger]:size-12!"
  >
    <template #calendar-heading="{ date, month }">
      <div class="flex gap-2 items-center">
        <div>Custom heading</div>
        <component :is="month" :date="date" />
      </div>
    </template>
    <template #prev-icon>
      <UiIcon name="chevrons-left" :size="20" class="text-red-500" />
    </template>
    <template #next-icon>
      <UiIcon name="chevrons-right" :size="20" class="text-red-500" />
    </template>
  </UiCalendar>
</template>`;

/**
 * Calendar with custom heading, larger cells, and custom navigation icons.
 */
export const CustomHeadingAndCellSize: Story = {
  args: {
    weekdayFormat: 'short',
  },
  render: (args) => ({
    components: { UiCalendar, UiIcon },
    setup() {
      const date = ref<DateValue>();
      const placeholder = ref(today(getLocalTimeZone()));
      return { date, placeholder, args };
    },
    template: `<UiCalendar
      v-model="date"
      :default-placeholder="placeholder"
      :weekday-format="args.weekdayFormat"
      :disabled="args.disabled"
      :locale="args.locale"
      class="rounded-md border shadow-sm **:data-[slot=calendar-cell-trigger]:size-12!"
    >
      <template #calendar-heading="{ date, month }">
        <div class="flex gap-2 items-center">
          <div>Custom heading</div>
          <component :is="month" :date="date" />
        </div>
      </template>
      <template #prev-icon>
        <UiIcon name="chevrons-left" :size="20" class="text-red-500" />
      </template>
      <template #next-icon>
        <UiIcon name="chevrons-right" :size="20" class="text-red-500" />
      </template>
    </UiCalendar>`,
  }),
  parameters: {
    docs: {
      source: {
        code: customHeadingTemplateSource,
      },
    },
  },
};
