import React from 'react'
import PropTypes from 'prop-types'
import slugify from 'slugify'
import shortid from 'shortid'
import Scroll from '../../components/Scroll'
import styles from './Entities.module.scss'
import { filterByKindOfProperty } from './utils'

const Type = ({ type }) => {
    let isArray = false
    if (type.type === 'array') {
        isArray = true
        type = type.elementType
    }
    const { name, type: typeOfType, typeArguments, id } = type
    const isInternal = typeOfType === 'reference' && id

    return (
        <div className={styles.type}>
            <span>
                {isInternal && (
                    <Scroll
                        type="id"
                        element={`${name && slugify(name)}`}
                        offset={-20}
                    >
                        {type.name}
                    </Scroll>
                )}
                {!isInternal && <span>{type.name}</span>}
            </span>

            {typeArguments && (
                <span>
                    <span className={styles.typeSymbol}>&lt;</span>
                    <span>
                        {typeArguments.map((typeArgument, i) => (
                            <span key={shortid.generate()}>
                                {i !== 0 && (
                                    <span className={styles.typeSymbol}>
                                        ,{' '}
                                    </span>
                                )}
                                <Type type={typeArgument} />
                            </span>
                        ))}
                    </span>
                    <span className={styles.typeSymbol}>&gt;</span>
                </span>
            )}

            {isArray && <span className={styles.typeSymbol}>[]</span>}
        </div>
    )
}

Type.propTypes = {
    type: PropTypes.object.isRequired
}

const PropertyDetails = ({ property }) => {
    const { type } = property
    return (
        <div>
            <Type type={type} />
        </div>
    )
}

PropertyDetails.propTypes = {
    property: PropTypes.object
}

const MethodDetails = ({ property }) => {
    const signature = property.signatures[0]
    const { parameters, type } = signature
    return (
        <>
            {parameters && parameters.length && (
                <div>
                    <h4 className={styles.subHeading}>Parameters</h4>

                    {parameters.map((parameter) => {
                        const { name, type, flags, comment } = parameter
                        const { isOptional } = flags
                        const description =
                            comment && (comment.text || comment.shortText)

                        return (
                            <div
                                className={styles.parameters}
                                key={shortid.generate()}
                            >
                                <h5>
                                    <code>{name}</code>
                                    {isOptional && (
                                        <span
                                            className={styles.parameterOptional}
                                            title="optional parameter"
                                        >
                                            ?
                                        </span>
                                    )}
                                </h5>
                                <Type type={type} />

                                <p>{description}</p>
                            </div>
                        )
                    })}
                </div>
            )}

            {type && (
                <div>
                    <h4 className={styles.subHeading}>Returns</h4>

                    <Type type={type} />
                </div>
            )}
        </>
    )
}

MethodDetails.propTypes = {
    property: PropTypes.object
}

const PropertyWrapper = ({ property, sourceUrl, parentAnchor }) => {
    const {
        name,
        kindString,
        flags,
        signatures,
        sources,
        decorators
    } = property
    const { isPublic, isStatic } = flags
    const signature = signatures && signatures[0]
    const comment = (signature && signature.comment) || property.comment
    const { fileName, line } = sources[0]
    const deprecation = (decorators || []).filter(
        ({ name }) => name === 'deprecated'
    )[0] // Assuming deprecated annotation
    let deprecatedUse, deprecatedSlug
    if (deprecation) {
        deprecatedUse = deprecation.arguments.alternative.replace(/('|")/g, '')
        deprecatedSlug =
            deprecatedUse && slugify(deprecatedUse.replace('.', '-'))
    }

    const sourceLink = `${sourceUrl}${fileName}#L${line}`

    return (
        <div
            className={styles.property}
            data-private={!isPublic}
            data-deprecated={!!deprecation}
            id={`${parentAnchor}-${name && slugify(name)}`}
        >
            <h3 className={styles.propertyName}>{name}</h3>

            <div
                className={styles.propertyType}
                data-type={kindString.toLowerCase()}
            >
                {kindString}
            </div>

            {isStatic && <div className={styles.propertyModifier}>static</div>}
            {!isPublic && (
                <div className={styles.propertyModifier} data-secondary>
                    private
                </div>
            )}

            {comment && !deprecation && (
                <div className={styles.propertyDescription}>
                    {comment.text || comment.shortText}
                </div>
            )}

            {deprecation && (
                <div className={styles.deprecation}>
                    <strong>Deprecated</strong>: use{' '}
                    <code>
                        <Scroll
                            type="id"
                            element={`${deprecatedSlug}`}
                            offset={-20}
                        >
                            {deprecatedUse}
                        </Scroll>
                    </code>{' '}
                    instead
                </div>
            )}

            {!deprecation &&
                (() => {
                    switch (kindString) {
                        case 'Method':
                            return <MethodDetails property={property} />
                        case 'Property':
                            return <PropertyDetails property={property} />
                    }
                })()}

            {fileName && (
                <a
                    className={styles.sourceLink}
                    href={sourceLink}
                    target="_blank"
                    rel="noopener noreferrer"
                >
                    {`${fileName}#L${line}`}
                </a>
            )}
        </div>
    )
}

PropertyWrapper.propTypes = {
    property: PropTypes.object,
    sourceUrl: PropTypes.string,
    parentAnchor: PropTypes.string
}

const Entities = ({ entities, sourceUrl }) =>
    entities.map(({ name, comment, children }) => (
        <div key={shortid.generate()} id={name && slugify(name)}>
            <h2 className={styles.entityName}>
                <code>{name}</code>
            </h2>

            {comment && (
                <div className={styles.entityDescription}>
                    {comment.text || comment.shortText}
                </div>
            )}

            {children &&
                children
                    .filter(filterByKindOfProperty)
                    .map((property) => (
                        <PropertyWrapper
                            key={shortid.generate()}
                            property={property}
                            sourceUrl={sourceUrl}
                            parentAnchor={name && slugify(name)}
                        />
                    ))}
        </div>
    ))

Entities.propTypes = {
    entities: PropTypes.array.isRequired,
    sourceUrl: PropTypes.string
}

export default Entities
