import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { onMounted, ref } from 'vue';
import { toast, UiSnackbar } from './index';
import { UiButton } from '../UiButton';
import { UiDialog, UiDialogContent, UiDialogTrigger } from '../UiDialog';

const meta: Meta<typeof UiSnackbar> = {
  title: 'Components/UiSnackbar',
  component: UiSnackbar,
  tags: ['autodocs'],
  argTypes: {
    position: {
      control: 'select',
      options: [
        'top-left',
        'top-right',
        'bottom-left',
        'bottom-right',
        'top-center',
        'bottom-center',
      ],
    },
    theme: {
      control: 'select',
      options: ['light', 'dark', 'system'],
    },
    richColors: {
      control: 'boolean',
      description:
        'High hierarchy (vivid background) when true, Low/Soft hierarchy when false.',
    },
    expand: { control: 'boolean' },
    closeButton: { control: 'boolean' },
    duration: { control: 'number' },
    visibleToasts: { control: 'number' },
    containerAriaLabel: { control: 'text' },
  },
  args: {
    position: 'bottom-right',
    theme: 'light',
    richColors: true,
    expand: false,
    closeButton: true,
    visibleToasts: 3,
    containerAriaLabel: 'Notifications',
  },
};

export default meta;
type Story = StoryObj<typeof UiSnackbar>;

const FIGMA_MESSAGE = 'You have a new notification.';
const FIGMA_ACTION_LABEL = 'Action';

const noopAction = {
  label: FIGMA_ACTION_LABEL,
  // eslint-disable-next-line no-console
  onClick: () => console.log('Action clicked'),
};

type TypedSnackbar = 'success' | 'info' | 'warning' | 'error';

function showTypedSnackbar(
  type: TypedSnackbar,
  toasterId: string,
  richColors: boolean,
) {
  return toast[type](FIGMA_MESSAGE, {
    toasterId,
    richColors,
    action: noopAction,
  });
}

function makeTypedStory(
  type: TypedSnackbar,
  hierarchy: 'high' | 'low',
  storyName: string,
): Story {
  const richColors = hierarchy === 'high';
  const toasterId = `story-${type}-${hierarchy}`;
  const buttonLabel = `${type[0].toUpperCase()}${type.slice(1)} ${
    hierarchy === 'high' ? 'High' : 'Low'
  }`;
  const sourceCode = `<script setup lang="ts">
import { UiSnackbar, toast, UiButton } from '@aleph-alpha/ui-library'
</script>

<template>
  <UiButton
    @click="toast.${type}('${FIGMA_MESSAGE}', {
      richColors: ${richColors},
      action: { label: '${FIGMA_ACTION_LABEL}', onClick: () => {} },
    })"
  >
    ${buttonLabel}
  </UiButton>
  <UiSnackbar close-button${richColors ? ' rich-colors' : ''} />
</template>`;

  return {
    name: storyName,
    args: { richColors, closeButton: true },
    render: (args) => ({
      components: { UiSnackbar, UiButton },
      setup() {
        const show = () => showTypedSnackbar(type, toasterId, args.richColors);
        return { args, show, toasterId, buttonLabel };
      },
      template: `
        <div>
          <UiButton @click="show">{{ buttonLabel }}</UiButton>
          <UiSnackbar v-bind="args" :id="toasterId" />
        </div>
      `,
    }),
    parameters: {
      docs: {
        source: { code: sourceCode },
      },
    },
  };
}

/**
 * Confirm that an operation succeeded - use as the primary success signal
 * (saved, sent, completed). High hierarchy: vivid green for emphasis.
 */
export const SuccessHigh: Story = makeTypedStory(
  'success',
  'high',
  'Success High',
);

/**
 * Confirm a non-critical success in a quieter tone - background-task
 * acknowledgements, autosave, sync complete.
 */
export const SuccessLow: Story = makeTypedStory(
  'success',
  'low',
  'Success Low',
);

/**
 * Surface neutral information that needs immediate attention - reminders,
 * tips, system announcements.
 */
export const InfoHigh: Story = makeTypedStory('info', 'high', 'Info High');

/**
 * Surface neutral information that doesn't compete with primary content -
 * inline help, contextual hints.
 */
export const InfoLow: Story = makeTypedStory('info', 'low', 'Info Low');

/**
 * Warn the user about a non-blocking caution - quota nearing limit, an action
 * has consequences. Use when the user can still proceed.
 */
export const WarningHigh: Story = makeTypedStory(
  'warning',
  'high',
  'Warning High',
);

/**
 * Quieter warning for informational caution that doesn't require immediate
 * action.
 */
export const WarningLow: Story = makeTypedStory(
  'warning',
  'low',
  'Warning Low',
);

/**
 * Report a failure or rejected action - upload failed, request errored, save
 * blocked. High hierarchy: vivid red for unmistakable signal.
 */
export const ErrorHigh: Story = makeTypedStory('error', 'high', 'Error High');

/**
 * Report a recoverable error in a less alarming tone - retry suggestions,
 * non-critical failures.
 */
export const ErrorLow: Story = makeTypedStory('error', 'low', 'Error Low');

const allVariantsTemplateSource = `<script setup lang="ts">
import { UiSnackbar, toast } from '@aleph-alpha/ui-library'

// Show one snackbar per type x hierarchy via toast.<type>(msg, { richColors })
toast.error('You have a new notification.', { richColors: true, action: { label: 'Action', onClick: () => {} } })
toast.info('You have a new notification.', { richColors: true, action: { label: 'Action', onClick: () => {} } })
// ... and so on for warning, success, plus richColors: false versions
</script>

<template>
  <UiSnackbar close-button rich-colors />
</template>`;

type MatrixCell = {
  type: TypedSnackbar;
  hierarchy: 'high' | 'low';
  id: string;
  label: string;
};

const ALL_CELLS: MatrixCell[] = (
  ['error', 'info', 'warning', 'success'] as const
).flatMap((type) =>
  (['low', 'high'] as const).map((hierarchy) => ({
    type,
    hierarchy,
    id: `matrix-${type}-${hierarchy}`,
    label: `${type[0].toUpperCase()}${type.slice(1)} ${
      hierarchy === 'high' ? 'High' : 'Low'
    }`,
  })),
);

/**
 * Side-by-side overview of every type x hierarchy combination from the Figma
 * matrix. Each cell mounts a real `<UiSnackbar />` and dispatches one toast
 * with `duration: Infinity` on mount, so what you see is the actual component,
 * not a static mock.
 */
export const AllVariants: Story = {
  name: 'AllVariants',
  parameters: {
    docs: {
      source: { code: allVariantsTemplateSource },
    },
  },
  render: () => ({
    components: { UiSnackbar },
    setup() {
      onMounted(() => {
        ALL_CELLS.forEach((cell) => {
          toast[cell.type](FIGMA_MESSAGE, {
            toasterId: cell.id,
            richColors: cell.hierarchy === 'high',
            duration: Infinity,
            action: noopAction,
          });
        });
      });
      return { cells: ALL_CELLS };
    },
    template: `
      <div class="snackbar-matrix grid grid-cols-1 md:grid-cols-2 gap-4 p-4">
        <div
          v-for="cell in cells"
          :key="cell.id"
          class="flex flex-col gap-2"
        >
          <span class="text-content-on-surface-secondary text-xs">
            {{ cell.label }}
          </span>
          <UiSnackbar
            :id="cell.id"
            :rich-colors="cell.hierarchy === 'high'"
            close-button
            expand
            :container-aria-label="'Notifications ' + cell.label"
            :visible-toasts="1"
          />
        </div>
      </div>
    `,
  }),
};

const defaultTemplateSource = `<script setup lang="ts">
import { UiSnackbar, toast, UiButton } from '@aleph-alpha/ui-library'
</script>

<template>
  <UiButton @click="toast('My first snackbar')">Show snackbar</UiButton>
  <UiSnackbar />
</template>`;

/**
 * Plain (untyped) snackbar without a status icon - use for neutral
 * confirmations that don't fit success/info/warning/error semantics. Falls
 * back to the project's inverse surface tokens for contrast.
 */
export const Default: Story = {
  args: { richColors: false, closeButton: false },
  render: (args) => ({
    components: { UiSnackbar, UiButton },
    setup() {
      const toasterId = 'story-default';
      const show = () => toast('My first snackbar', { toasterId });
      return { args, show, toasterId };
    },
    template: `
      <div>
        <UiButton @click="show">Show snackbar</UiButton>
        <UiSnackbar v-bind="args" :id="toasterId" />
      </div>
    `,
  }),
  parameters: {
    docs: {
      source: { code: defaultTemplateSource },
    },
  },
};

const withDescriptionTemplateSource = `<script setup lang="ts">
import { UiSnackbar, toast, UiButton } from '@aleph-alpha/ui-library'
</script>

<template>
  <UiButton
    @click="toast('Event has been created', {
      description: 'Sunday, December 03, 2023 at 9:00 AM',
      action: { label: 'Undo', onClick: () => console.log('Undo') },
    })"
  >
    Show snackbar
  </UiButton>
  <UiSnackbar />
</template>`;

/**
 * Use when the message needs supporting context (timestamp, location, related
 * record). Not in the current Figma design but supported by the underlying API.
 */
export const WithDescription: Story = {
  args: { richColors: false, closeButton: false },
  render: (args) => ({
    components: { UiSnackbar, UiButton },
    setup() {
      const toasterId = 'story-description';
      const show = () =>
        toast('Event has been created', {
          toasterId,
          description: 'Sunday, December 03, 2023 at 9:00 AM',
          action: {
            label: 'Undo',
            // eslint-disable-next-line no-console
            onClick: () => console.log('Undo'),
          },
        });
      return { args, show, toasterId };
    },
    template: `
      <div>
        <UiButton @click="show">Show snackbar</UiButton>
        <UiSnackbar v-bind="args" :id="toasterId" />
      </div>
    `,
  }),
  parameters: {
    docs: {
      source: { code: withDescriptionTemplateSource },
    },
  },
};

const promiseTemplateSource = `<script setup lang="ts">
import { UiSnackbar, toast, UiButton } from '@aleph-alpha/ui-library'

function handlePromiseClick() {
  toast.promise<{ name: string }>(
    () => new Promise((resolve) => setTimeout(() => resolve({ name: 'Event' }), 2000)),
    {
      loading: 'Loading...',
      success: (data) => \`\${data.name} has been created\`,
      error: 'Error',
    },
  )
}
</script>

<template>
  <UiButton @click="handlePromiseClick">Show promise snackbar</UiButton>
  <UiSnackbar />
</template>`;

function resolveAfter<T>(value: T, ms: number): Promise<T> {
  return new window.Promise((resolve) => setTimeout(() => resolve(value), ms));
}

/**
 * Use to track an async operation - shows loading, then transitions to success
 * or error based on the promise outcome. Good for save/upload flows where you
 * want one snackbar to tell the whole story.
 */
export const PromiseSnackbar: Story = {
  name: 'Promise',
  render: (args) => ({
    components: { UiSnackbar, UiButton },
    setup() {
      const toasterId = 'story-promise';
      const show = () => {
        toast.promise<{ name: string }>(
          () => resolveAfter({ name: 'Event' }, 2000),
          {
            toasterId,
            loading: 'Loading...',
            success: (data) => `${data.name} has been created`,
            error: 'Error',
          },
        );
      };
      return { args, show, toasterId };
    },
    template: `
      <div>
        <UiButton @click="show">Show promise snackbar</UiButton>
        <UiSnackbar v-bind="args" :id="toasterId" />
      </div>
    `,
  }),
  parameters: {
    docs: {
      source: { code: promiseTemplateSource },
    },
  },
};

const withDialogTemplateSource = `<script setup lang="ts">
import {
  UiSnackbar,
  toast,
  UiButton,
  UiDialog,
  UiDialogTrigger,
  UiDialogContent,
} from '@aleph-alpha/ui-library'

function handleInteractOutside(event: Event) {
  const target = event.target as HTMLElement
  if (target?.closest('[data-sonner-toaster]')) event.preventDefault()
}
</script>

<template>
  <UiDialog>
    <UiDialogTrigger as-child>
      <UiButton variant="outline">Open dialog</UiButton>
    </UiDialogTrigger>
    <UiDialogContent
      title="Confirm action"
      @interact-outside="handleInteractOutside"
    >
      <UiButton
        @click="toast.success('${FIGMA_MESSAGE}', {
          action: { label: '${FIGMA_ACTION_LABEL}', onClick: () => {} },
        })"
      >
        Show snackbar
      </UiButton>
    </UiDialogContent>
  </UiDialog>
  <UiSnackbar rich-colors close-button />
</template>`;

/**
 * Trigger a snackbar from inside a modal dialog. Forward `@interact-outside`
 * so clicking the snackbar doesn't dismiss the dialog (the toast renders
 * outside the dialog tree).
 */
export const WithDialog: Story = {
  render: (args) => ({
    components: {
      UiSnackbar,
      UiButton,
      UiDialog,
      UiDialogTrigger,
      UiDialogContent,
    },
    setup() {
      const open = ref(false);
      const toasterId = 'story-dialog';
      const show = () => showTypedSnackbar('success', toasterId, true);
      const handleInteractOutside = (event: Event) => {
        const target = event.target as HTMLElement | null;
        if (target?.closest('[data-sonner-toaster]')) event.preventDefault();
      };
      return { args, open, show, handleInteractOutside, toasterId };
    },
    template: `
      <div>
        <UiDialog v-model:open="open">
          <UiDialogTrigger as-child>
            <UiButton variant="outline">Open dialog</UiButton>
          </UiDialogTrigger>
          <UiDialogContent
            title="Confirm action"
            description="Trigger a snackbar from inside the dialog."
            @interact-outside="handleInteractOutside"
          >
            <UiButton @click="show">Show snackbar</UiButton>
          </UiDialogContent>
        </UiDialog>
        <UiSnackbar v-bind="args" :id="toasterId" />
      </div>
    `,
  }),
  parameters: {
    docs: {
      source: { code: withDialogTemplateSource },
    },
  },
};
