import React from 'react'
import { type Meta, type StoryObj } from '@storybook/react-vite'
import { Link } from 'react-router-dom'
import { expect } from 'storybook/test'

import Button from './Button'
import { toast } from '../Toast/Toast'
import { DocsTemplate } from '../../../.storybook'

const figmaLink =
  'https://www.figma.com/design/RvhKD82948FMQnh5MyCi0o/Web-Design-System?node-id=121-335&t=47NWXExemisHjPrj-0'

const meta: Meta<typeof Button> = {
  title: 'Components/Buttons/Button',
  component: Button,
  parameters: {
    design: {
      type: 'figma',
      url: figmaLink,
    },
    docs: {
      page: () => (
        <DocsTemplate
          description='The Button component is a versatile element used to represent actionable items throughout the applications. It serves as a clickable interface element that triggers a specific action or interaction.'
          infoBullets={[
            <span key='first-bullet'>
              <code>as='button'</code> - This is a standard button that triggers
              an action within the application.
            </span>,
            <span key='second-bullet'>
              <code>as='link'</code> - This is used when the button is an
              internal navigational link.
            </span>,
            <span key='third-bullet'>
              <code>as='externalLink'</code> - This is used when the button is
              an external link that will navigate outside of the application.
            </span>,
            <span key='forth-bullet'>
              <code>as='unstyled'</code> - This is used when you need an element
              to be clickable, but do not want to inherit the generic button
              styles.
            </span>,
          ]}
          figmaLink={figmaLink}
        />
      ),
    },
  },
}

export default meta

type Story = StoryObj<typeof Button>

const Template: Story = {
  render: (args) => <Button {...args} />,
}

export const Primary: Story = {
  ...Template,
  args: {
    styleType: 'primary',
    as: 'button',
    children: 'Primary',
    disabled: false,
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('primary')
    await expect(buttonElement).toHaveTextContent('Primary')
  },
}

export const PrimaryDestructive: Story = {
  ...Template,
  args: {
    as: 'button',
    styleType: 'primary',
    children: 'Destructive',
    destructive: true,
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('primary')
    await expect(buttonElement?.className).toContain('destructive')
    await expect(buttonElement).toHaveTextContent('Destructive')
  },
}

export const Secondary: Story = {
  ...Template,
  args: {
    styleType: 'secondary',
    as: 'button',
    children: 'Secondary',
    disabled: false,
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('secondary')
    await expect(buttonElement).toHaveTextContent('Secondary')
  },
}

export const Tertiary: Story = {
  ...Template,
  args: {
    styleType: 'tertiary',
    as: 'button',
    children: 'Tertiary',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('tertiary')
    await expect(buttonElement).toHaveTextContent('Tertiary')
  },
}

export const TextRed: Story = {
  ...Template,
  args: {
    styleType: 'text-red',
    as: 'button',
    children: 'Text Red',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('text-red')
    await expect(buttonElement).toHaveTextContent('Text Red')
  },
}

export const TextBlue: Story = {
  ...Template,
  args: {
    styleType: 'text-blue',
    as: 'button',
    children: 'Text Blue',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('text-blue')
    await expect(buttonElement).toHaveTextContent('Text Blue')
  },
}

export const Unstyled: Story = {
  ...Template,
  args: {
    as: 'unstyled',
    children: 'Unstyled',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('unstyled')
    await expect(buttonElement).toHaveTextContent('Unstyled')
  },
}

export const DisabledButtonWithTooltip: Story = {
  ...Template,
  args: {
    styleType: 'secondary',
    as: 'button',
    children: 'Secondary',
    disabled: true,
    tooltip: {
      tooltipContent: (
        <span>
          Tooltip will appear when button is disabled and tooltip present
        </span>
      ),
      position: 'top',
    },
  },
  play: async ({ canvas, userEvent }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()
    const buttonElement = button.querySelector('button')
    await expect(buttonElement?.className).toContain('secondary')
    await expect(buttonElement?.className).toContain('disabled')
    await expect(buttonElement).toHaveTextContent('Secondary')
    await expect(buttonElement).toBeDisabled()

    // ToDo- The tooltip component needs to be checked properly.
    // await userEvent.hover(button)

    // const tooltip = document.body.querySelector('[role="tooltip"]')

    // await expect(tooltip).toBeInTheDocument()
    // await expect(tooltip).toHaveTextContent(
    //   'Tooltip will appear when button is disabled and tooltip present',
    // )

    // await userEvent.unhover(button)
  },
}

export const WithConfirmation: Story = {
  ...Template,
  args: {
    as: 'confirmation',
    children: 'With Confirmation',
    confirmation: {
      header: 'Are you sure?',
      type: 'green',
      body: 'This will perform some action. Would you like to continue?',
      confirmCallout: () => {
        toast({ message: 'You did it!', type: 'success' })
      },
    },
  },
  play: async ({ canvas, userEvent }) => {
    const closeButton = canvas.getByRole('button')
    await expect(closeButton).toHaveTextContent('With Confirmation')
    await userEvent.click(closeButton)

    // Since popover is rendered outside canvas, we need to use document.body
    const popover1 = document.body.querySelector('[role="dialog"]')
    await expect(popover1).toBeInTheDocument()

    const confirmButton = Array.from(
      popover1?.querySelectorAll('button') ?? [],
    ).find((btn) => btn.textContent?.toLowerCase().includes('confirm'))
    await expect(confirmButton).toBeInTheDocument()

    // Test popover content
    await expect(popover1).toHaveTextContent('Are you sure?')
    await expect(popover1).toHaveTextContent(
      'This will perform some action. Would you like to continue?',
    )

    await userEvent.click(confirmButton!)
    await expect(canvas.getByText('You did it!')).toBeInTheDocument()

    await userEvent.click(closeButton)
    const popover2 = document.body.querySelector('[role="dialog"]')
    await expect(popover2).toBeInTheDocument()
    const closeButton1 = Array.from(
      popover2?.querySelectorAll('button') ?? [],
    ).find((btn) => btn.textContent?.toLowerCase().includes('cancel'))
    await expect(closeButton1).toBeInTheDocument()
    await userEvent.click(closeButton1!)
  },
}

export const ConfirmationButtonText: Story = {
  ...Template,
  args: {
    as: 'confirmation',
    children: 'With Confirmation',
    confirmation: {
      header: 'Are you sure?',
      type: 'green',
      body: 'This will perform some action. Would you like to continue?',
      confirmCallout: () => {
        toast({ message: 'You did it!', type: 'success' })
      },
      cancelButtonText: 'No',
      confirmButtonText: 'Yes',
    },
  },
  play: async ({ canvas, userEvent }) => {
    const closeButton = canvas.getByRole('button')
    await expect(closeButton).toHaveTextContent('With Confirmation')
    await userEvent.click(closeButton)

    // Since popover is rendered outside canvas, we need to use document.body
    const popover1 = document.body.querySelector('[role="dialog"]')
    await expect(popover1).toBeInTheDocument()

    const confirmButton = Array.from(
      popover1?.querySelectorAll('button') ?? [],
    ).find((btn) => btn.textContent?.toUpperCase().includes('YES'))
    await expect(confirmButton).toBeInTheDocument()
    await expect(confirmButton?.className).toContain('green')

    // Test popover content
    await expect(popover1).toHaveTextContent('Are you sure?')
    await expect(popover1).toHaveTextContent(
      'This will perform some action. Would you like to continue?',
    )

    await userEvent.click(confirmButton!)
    await expect(canvas.getByText('You did it!')).toBeInTheDocument()

    await userEvent.click(closeButton!)
    const popover2 = document.body.querySelector('[role="dialog"]')
    await expect(popover2).toBeInTheDocument()
    const closeButton1 = Array.from(
      popover2?.querySelectorAll('button') ?? [],
    ).find((btn) => btn.textContent?.toUpperCase().includes('NO'))
    await expect(closeButton1).toBeInTheDocument()
    await userEvent.click(closeButton1!)
  },
}

export const LinkButton: Story = {
  ...Template,
  args: {
    as: 'link',
    routerComponent: Link,
    children: 'Link',
    to: '/',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()

    const linkElement = button.querySelector('a')
    await expect(linkElement).toBeInTheDocument()
    await expect(linkElement?.className).toContain('secondary') // default styleType
    await expect(linkElement?.className).toContain('link')
    await expect(linkElement).toHaveTextContent('Link')
  },
}

export const DestructiveLink: Story = {
  ...Template,
  args: {
    as: 'link',
    styleType: 'primary',
    destructive: true,
    children: 'Destructive Link',
    routerComponent: Link,
    to: '/destructive-action',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()

    const linkElement = button.querySelector('a')
    await expect(linkElement).toBeInTheDocument()
    await expect(linkElement?.className).toContain('primary')
    await expect(linkElement?.className).toContain('destructive')
    await expect(linkElement).toHaveTextContent('Destructive Link')
    await expect(linkElement).toHaveAttribute('href', '/destructive-action')
  },
}

export const ExternalLinkButton: Story = {
  ...Template,
  args: {
    as: 'externalLink',
    styleType: 'text-blue',
    children: 'External Link',
    href: 'https://pattern.com',
  },
  play: async ({ canvas }) => {
    const button = canvas.getByTestId('button')
    await expect(button).toBeInTheDocument()

    const linkElement = button.querySelector('a')
    await expect(linkElement).toBeInTheDocument()
    await expect(linkElement?.className).toContain('text-blue')
    await expect(linkElement?.className).toContain('link')
    await expect(linkElement).toHaveTextContent('External Link')
    await expect(linkElement).toHaveAttribute('href', 'https://pattern.com')
    await expect(linkElement).toHaveAttribute('target', '_blank')
    await expect(linkElement).toHaveAttribute('rel', 'noopener noreferrer')
  },
}

export const ButtonWithCustomOnMouseLeave: Story = {
  ...Template,
  args: {
    styleType: 'secondary',
    as: 'link',
    children: 'Hover to Test onMouseLeave',
    tooltip: {
      tooltipContent:
        'This button has a custom onMouseLeave handler that works with tooltips!',
      position: 'top',
    },
    onMouseLeave: () => {
      toast({
        message:
          'Custom onMouseLeave handler called! Tooltip should still work.',
        type: 'info',
      })
    },
  },
}
