'use client'

import React, { cloneElement, isValidElement, ReactNode, useRef, useState } from 'react'
import reactElementToJSXString from 'react-element-to-jsx-string'
import * as prettier from 'prettier/standalone'
import * as parserHtml from 'prettier/parser-html'
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'
import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx'
import { prism } from 'react-syntax-highlighter/dist/esm/styles/prism'
import { PktButton } from '../button/Button'
import { PktCheckbox } from '../checkbox/Checkbox'
import { PktTabs } from '../tabs/Tabs'
import { PktTag } from '../tag/Tag'
import { PktTextinput } from '../textinput/Textinput'
import { PktPreviewSpecs, ISpecObject } from './PreviewSpecs'
import { PktPreviewPropEditor } from './PreviewPropEditor'

SyntaxHighlighter.registerLanguage('jsx', jsx)

interface PreviewProps {
  children?: ReactNode
  specs: ISpecObject
  fullWidth?: boolean
}

export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth }) => {
  const initialProps =
    specs.props && typeof specs.props === 'object' && !Array.isArray(specs.props)
      ? Object.entries(specs.props).reduce<Record<string, any>>((acc, [key, value]) => {
          if (
            typeof value === 'object' &&
            !Array.isArray(value) &&
            value.previewDefault !== undefined &&
            value.previewDefault !== null
          ) {
            if (value.previewDefault === 'false') {
              value.previewDefault = false
            }
            if (value.previewDefault === 'true') {
              value.previewDefault = true
            }
            acc[key] = value.previewDefault
          } else if (
            typeof value === 'object' &&
            !Array.isArray(value) &&
            value.default !== undefined &&
            value.default !== null
          ) {
            if (value.default === 'false') {
              value.default = false
            }
            if (value.default === 'true') {
              value.default = true
            }
            acc[key] = value.default
          }
          return acc
        }, {})
      : {}
  const [iteratedKey, setIteratedKey] = useState(0)
  const [props, setProps] = useState(initialProps)
  const [mode, setMode] = useState<'light' | 'dark'>('light')
  const [htmlContent, setHtmlContent] = useState('')
  const [jsxContent, setJsxContent] = useState('')
  const [copied, setCopied] = useState('')
  const [slotContent, setSlotContent] = useState('Rediger innhold')
  const [tabs, setTabs] = useState([
    {
      text: 'Rediger',
      icon: 'adjust',
      active: true,
    },
    {
      text: 'Kode (React)',
      icon: 'react',
      active: false,
    },
    {
      text: `Kode (${specs.isElement ? 'Element' : 'HTML'})`,
      icon: 'code',
      active: false,
    },
    {
      text: 'Egenskaper',
      icon: 'list',
      active: false,
    },
  ])

  const changeContent = (id: number) => {
    if (id === 1) {
      fetchJsx()
    }
    if (id === 2) {
      fetchHtml()
    }
    setCopied('')
    setTabs((prevTabs) =>
      prevTabs.map((tab, index) => ({
        ...tab,
        active: index === id,
      })),
    )
  }

  const previewComponent = useRef<any>(null)

  const fetchHtml = async () => {
    if (specs.isElement && isValidElement(component)) {
      const componentType = isValidElement(component)
        ? (component.type as any).displayName || (component.type as any).name
        : specs.name

      const attributes = Object.entries(props)
        .map(([key, value]) => {
          if (value && !isEmptyArray(value) && !isEmptyObject(value)) {
            return typeof value === 'object' ? `${key}='${JSON.stringify(value)}'` : `${key}="${value}"`
          }
        })
        .join(' ')

      const customElement = await prettier.format(
        `<${specs.name || componentType} ${attributes}>${slotContent}</${specs.name || componentType}>`,
        {
          parser: 'html',
          plugins: [parserHtml as any],
        },
      )

      setHtmlContent(
        '<!-- Denne koden bør testes grundig før bruk. Spør en Punkt-utvikler om du er usikker. -->\n' + customElement,
      )
    } else {
      const html = await prettier.format(previewComponent.current.innerHTML, {
        parser: 'html',
        plugins: [parserHtml as any],
      })
      setHtmlContent(html.replace(/<\!--.*?-->/g, ''))
    }
  }

  const fetchJsx = async () => {
    const jsx = await reactElementToJSXString(component)
    setJsxContent(jsx)
    return Promise.resolve()
  }

  const copyToClipboard = (type: string, content: string) => {
    navigator.clipboard.writeText(content).then(() => {
      setCopied(type)
      console.log('Copied to clipboard', type)
    })
  }

  const isEmptyArray = (arr: any) => Array.isArray(arr) && arr.length === 0
  const isEmptyObject = (obj: any) => obj && typeof obj === 'object' && !Object.keys(obj).length

  const CopyButton = (type: string, content: string) => (
    <div className="pkt-preview__copy">
      {copied === type && <span className="pkt-preview__copied">Kode kopiert til utklippstavle</span>}
      <PktButton
        skin="tertiary"
        variant="icon-only"
        size="small"
        iconName="copy"
        onClick={() => copyToClipboard(type, content)}
      >
        Kopier kode
      </PktButton>
    </div>
  )

  const component =
    isValidElement(children) &&
    cloneElement(
      children,
      { ...props },
      children.props.children ? children.props.children : specs.slots?.default ? slotContent : null,
    )

  const handleChange = (key: string, value: any, displayAsFalse: boolean = false) => {
    setIteratedKey(iteratedKey + 1)
    if (!displayAsFalse && (!value || value == 'false')) {
      const { [key]: _, ...rest } = props
      setProps(rest)
    } else {
      setProps((prevProps) => ({
        ...prevProps,
        [key]: value,
      }))
    }
  }

  return (
    <div className="pkt-preview">
      <div className="pkt-preview__component-container" data-mode={mode}>
        {specs['dark-mode'] && (
          <div className="pkt-preview__mode">
            <PktCheckbox
              id="mode"
              label="Dark mode"
              type="checkbox"
              checked={mode === 'dark'}
              onChange={(e) => setMode(e.target.checked ? 'dark' : 'light')}
              labelPosition="right"
              isSwitch
            />
          </div>
        )}
        <div
          className={`pkt-preview__component ${fullWidth && 'pkt-preview__component--fullwidth'}`}
          ref={previewComponent}
          key={iteratedKey}
        >
          <div>{component}</div>
        </div>
      </div>
      <PktTabs tabs={tabs} onTabSelected={changeContent}></PktTabs>
      <div className={`pkt-grid pkt-preview__opts ${tabs[0].active ? '' : 'pkt-hide'}`}>
        {specs.slots?.default && !(children as React.ReactElement).props.children && (
          <div className="pkt-cell pkt-cell--span12 pkt-cell--span6-phablet-up">
            <PktTag size="small" skin="blue-light" textStyle="thin-text">
              children
            </PktTag>
            <PktTextinput
              id="slot"
              label="Slot"
              helptext={specs.slots.default.description || 'Innholdet i slot'}
              type="text"
              value={slotContent}
              onChange={(e) => setSlotContent(e.currentTarget.value)}
            />
          </div>
        )}
        {specs.props && typeof specs.props === 'object' && !Array.isArray(specs.props) ? (
          Object.entries(specs.props).map(([key, prop]) => (
            <div className="pkt-cell pkt-cell--span12 pkt-cell--span6-phablet-up" key={key}>
              <PktTag size="small" skin="blue-light" textStyle="thin-text">
                {key}
              </PktTag>
              <PktPreviewPropEditor
                propKey={key}
                props={props}
                spec={prop}
                handleChange={handleChange}
              ></PktPreviewPropEditor>
            </div>
          ))
        ) : (
          <p>OBS! Specs mangler props!</p>
        )}
      </div>
      <div className={`pkt-preview__code ${tabs[1].active ? '' : 'pkt-hide'}`}>
        <SyntaxHighlighter language="jsx" style={prism}>
          {jsxContent}
        </SyntaxHighlighter>
        {CopyButton('jsx', jsxContent)}
      </div>
      <div className={`pkt-preview__code ${tabs[2].active ? '' : 'pkt-hide'}`}>
        <SyntaxHighlighter language="html" style={prism}>
          {htmlContent}
        </SyntaxHighlighter>
        {CopyButton('html', htmlContent)}
      </div>
      <div className={`pkt-preview__specs ${tabs[3].active ? '' : 'pkt-hide'}`}>
        <PktPreviewSpecs specs={specs}></PktPreviewSpecs>
      </div>
    </div>
  )
}

PktPreview.displayName = 'PktPreview'
