import {
  ButtonHTMLAttributes,
  DetailedHTMLProps,
  Dispatch,
  HTMLAttributes,
  SetStateAction,
  createContext,
  useContext,
  useId,
  useState,
} from 'react'

interface IAccordion {
  id: string
  open: boolean
  toggle: Dispatch<SetStateAction<boolean>>
}

const AccordionContext = createContext<IAccordion>({
  id: '',
  open: false,
  toggle: () => {},
})

interface AccordionProps
  extends Omit<
    DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>,
    'children'
  > {
  defaultOpen?: boolean
  children: React.ReactNode | ((props: { open: boolean }) => React.ReactNode)
}

export const Accordion = ({
  children,
  defaultOpen = false,
  ...rest
}: AccordionProps) => {
  const [open, toggle] = useState(defaultOpen)
  const id = useId()

  return (
    <AccordionContext.Provider value={{ id, open, toggle }}>
      <section {...rest}>
        {typeof children === 'function' ? children({ open }) : children}
      </section>
    </AccordionContext.Provider>
  )
}

interface HeaderProps
  extends DetailedHTMLProps<
    ButtonHTMLAttributes<HTMLButtonElement>,
    HTMLButtonElement
  > {}

const Header = ({ children, className, ...rest }: HeaderProps) => {
  const { id, open, toggle } = useContext(AccordionContext)

  return (
    <header>
      <h3>
        <button
          aria-expanded={open}
          aria-controls={id}
          onClick={() => toggle(!open)}
          className={`w-full text-left ${className}`}
          {...rest}
        >
          {children}
        </button>
      </h3>
    </header>
  )
}

interface BodyProps
  extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {}

const Body = ({ children, className, ...rest }: BodyProps) => {
  const { id, open } = useContext(AccordionContext)

  return (
    <div
      id={id}
      role="region"
      aria-hidden={!open}
      aria-labelledby={id}
      tabIndex={open ? 0 : -1}
      className={`${className} ${open ? 'block' : 'hidden'}`}
      {...rest}
    >
      {children}
    </div>
  )
}

Accordion.Header = Header
Accordion.Body = Body

/**
 * Usage
 * 
 <Accordion>
    <Accordion.Header>Header</Accordion.Header>
    <Accordion.Body>Body</Accordion.Body>
  </Accordion>
  ----------------------------
  <Accordion>
    {({ open }) => (
      <>
        <Accordion.Header>Header</Accordion.Header>
        <Accordion.Body>Body</Accordion.Body>
      </>
    )}
  </Accordion>
 */

