import { Anchor, Typography } from 'antd'
import Title from 'antd/lib/typography/Title'
import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState
} from 'react'
import type { CSSProperties, ReactNode } from 'react'

const { Text } = Typography

type FormSectionItem = {
  id: string
  title: string
  element: HTMLElement
  hidden?: boolean
}

type FormSectionContextValue = {
  registerSection: (section: FormSectionItem) => void
  unregisterSection: (id: string) => void
}

const FormSectionContext = createContext<FormSectionContextValue | null>(null)

const sectionStyle: CSSProperties = {
  border: '1px solid #d9d9d9',
  borderRadius: '8px',
  padding: '20px 24px',
  marginBottom: '16px',
  background: '#fff'
}

const titleStyle: CSSProperties = {
  marginTop: 0,
  marginBottom: '4px'
}

const descriptionStyle: CSSProperties = {
  display: 'block',
  marginBottom: '12px',
  fontSize: '12px'
}

const optionStyle: CSSProperties = {
  marginBottom: '12px'
}

const titleRowStyle: CSSProperties = {
  display: 'flex',
  alignItems: 'baseline',
  gap: '8px'
}

const optionTitleRowStyle: CSSProperties = {
  ...titleRowStyle,
  marginBottom: '4px'
}

const infoLinkStyle: CSSProperties = {
  fontSize: '12px'
}

const requiredMarkStyle: CSSProperties = {
  color: '#ff4d4f',
  marginRight: '4px'
}

const groupStyle: CSSProperties = {
  display: 'flex',
  alignItems: 'flex-start',
  gap: '16px'
}

const groupContentStyle: CSSProperties = {
  flex: 1,
  minWidth: 0
}

const topOutlineStyle: CSSProperties = {
  position: 'sticky',
  top: 0,
  zIndex: 1,
  marginBottom: '16px',
  padding: '8px 0',
  borderBottom: '1px solid #f0f0f0',
  background: '#fff'
}

const sideOutlineStyle: CSSProperties = {
  position: 'sticky',
  top: '16px',
  width: '170px',
  maxHeight: 'calc(100vh - 32px)',
  overflow: 'auto',
  padding: '8px 0',
  background: '#fff'
}

let sectionIdSeed = 0

const buildSectionId = (title: string) => {
  sectionIdSeed += 1
  return `form-section-${title
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-|-$/g, '')}-${sectionIdSeed}`
}

const sortSections = (sections: FormSectionItem[]) => {
  return [...sections].sort((a, b) => {
    if (a.element === b.element) {
      return 0
    }

    return a.element.compareDocumentPosition(b.element) &
      Node.DOCUMENT_POSITION_PRECEDING
      ? 1
      : -1
  })
}

const InfoLink = ({ info_link }: { info_link?: string }) => {
  if (!info_link) {
    return null
  }

  return (
    <Typography.Link
      href={info_link}
      target="_blank"
      rel="noreferrer"
      style={infoLinkStyle}
    >
      Info
    </Typography.Link>
  )
}

export const FormSectionGroup = ({
  show_outline,
  outline_position = 'left',
  children
}: {
  show_outline?: boolean
  outline_position?: 'left' | 'right' | 'top'
  children: ReactNode
}) => {
  const [sections, setSections] = useState<FormSectionItem[]>([])

  const registerSection = useCallback((section: FormSectionItem) => {
    setSections((prevSections) => {
      const nextSections = prevSections.filter((item) => item.id !== section.id)
      return sortSections([...nextSections, section])
    })
  }, [])

  const unregisterSection = useCallback((id: string) => {
    setSections((prevSections) => prevSections.filter((item) => item.id !== id))
  }, [])

  const contextValue = useMemo(
    () => ({ registerSection, unregisterSection }),
    [registerSection, unregisterSection]
  )

  const visibleSections = sections.filter((section) => !section.hidden)
  const showLeftOutline = show_outline && outline_position === 'left'
  const showRightOutline = show_outline && outline_position === 'right'
  const showSideOutline = showLeftOutline || showRightOutline
  const showTopOutline = show_outline && outline_position === 'top'
  const outline =
    show_outline && visibleSections.length > 0 ? (
      <Anchor
        affix={false}
        style={showSideOutline ? sideOutlineStyle : topOutlineStyle}
      >
        {visibleSections.map((section) => (
          <Anchor.Link
            key={section.id}
            href={`#${section.id}`}
            title={section.title}
          />
        ))}
      </Anchor>
    ) : null

  return (
    <FormSectionContext.Provider value={contextValue}>
      <div style={showSideOutline ? groupStyle : undefined}>
        {showLeftOutline && outline}
        {showTopOutline && outline}
        <div style={showSideOutline ? groupContentStyle : undefined}>
          {children}
        </div>
        {showRightOutline && outline}
      </div>
    </FormSectionContext.Provider>
  )
}

export const FormSection = ({
  title,
  info_link,
  description,
  children,
  outline_title,
  hide_in_outline,
  id,
  bordered = true
}: {
  title?: string
  info_link?: string
  description?: string
  children: ReactNode
  outline_title?: string
  hide_in_outline?: boolean
  id?: string
  bordered?: boolean
}) => {
  const sectionContext = useContext(FormSectionContext)
  const sectionRef = useRef<HTMLDivElement>(null)
  const sectionId = useRef<string>()
  const sectionTitle = outline_title || title

  if (!sectionId.current) {
    sectionId.current = id || buildSectionId(sectionTitle || 'section')
  }

  const currentSectionId = sectionId.current

  useEffect(() => {
    if (!sectionContext || !sectionRef.current) {
      return undefined
    }

    if (!sectionTitle) {
      return undefined
    }

    sectionContext.registerSection({
      id: currentSectionId,
      title: sectionTitle,
      hidden: hide_in_outline,
      element: sectionRef.current
    })

    return () => {
      sectionContext.unregisterSection(currentSectionId)
    }
  }, [currentSectionId, hide_in_outline, sectionContext, sectionTitle])

  return (
    <div
      id={currentSectionId}
      ref={sectionRef}
      style={{ ...sectionStyle, border: bordered ? sectionStyle.border : 0 }}
    >
      {(title || info_link) && (
        <div style={titleRowStyle}>
          {title && (
            <Title level={5} style={titleStyle}>
              {title}
            </Title>
          )}
          <InfoLink info_link={info_link} />
        </div>
      )}
      {description && (
        <Text type="secondary" style={descriptionStyle}>
          {description}
        </Text>
      )}
      {children}
    </div>
  )
}

export const FormOption = ({
  title,
  info_link,
  description,
  required,
  children,
  style
}: {
  title: string
  info_link?: string
  description?: string
  required?: boolean
  children: ReactNode
  style?: CSSProperties
}) => {
  return (
    <div style={{ ...optionStyle, ...style }}>
      <div style={optionTitleRowStyle}>
        <Text strong>
          {required && <span style={requiredMarkStyle}>*</span>}
          {title}
        </Text>
        <InfoLink info_link={info_link} />
      </div>
      <Text type="secondary" style={descriptionStyle}>
        {description}
      </Text>
      {children}
    </div>
  )
}
