import Button from '@atlaskit/button/new';
import Heading from '@atlaskit/heading';
import Modal, { ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
import type { FunctionComponent } from 'react';
import React, { memo, useState } from 'react';
import { Element, Link } from 'react-scroll';

import type {
    SerializedDocument,
    SerializedEnumType,
    SerializedObjectType,
    SerializedScalar,
    SerializedUnionType,
    // We only import types here
    // eslint-disable-next-line node/no-unpublished-import
} from '@atlassian/clientside-extensions-schema';
import useDiscovery from './debug/useDiscovery';
import { Blinker, BlinkerContainer, GRID_SIZE, HeadCell, HeadingWrapper, HeadRow, Table, TableCell, TableRow } from './styled';

export type ExtensionPointInfoProps = {
    name: string;
    schemaDocuments: { schema: SerializedDocument; contextSchema: SerializedDocument };
};

const LinkToElement = ({ to }: { to: string }) => {
    // TODO: is there a way to get the clean type from the schema?
    // We are removing the e.g. "!" required syntax that is a suffix of the schema type
    const cleanTo = to.replace(/[^a-zA-Z0-9]/g, '');

    return (
        // eslint-disable-next-line jsx-a11y/anchor-is-valid, @typescript-eslint/ban-ts-comment
        // @ts-ignore: react-scroll types are not up to date with the latest react version
        <Link to={cleanTo} href="" containerId="___cse_info_container" offset={-20} smooth>
            {to}
        </Link>
    );
};

const TypeRenderer: FunctionComponent<{ objectType: SerializedObjectType }> = ({ objectType }) => (
    <>
        <p>{objectType.description}</p>
        <Table>
            <thead>
                <HeadRow>
                    <HeadCell>Name</HeadCell>
                    <HeadCell>Type</HeadCell>
                    <HeadCell>Required</HeadCell>
                    <HeadCell>Description</HeadCell>
                </HeadRow>
            </thead>
            <tbody>
                {objectType.descriptors.map((descriptor) => (
                    <TableRow key={descriptor.name}>
                        <TableCell width="15%"> {descriptor.name}</TableCell>
                        <TableCell width="15%">
                            <LinkToElement to={descriptor.type} />
                        </TableCell>
                        <TableCell width="10%">{descriptor.required ? 'true' : 'false'}</TableCell>
                        <TableCell>{descriptor.description}</TableCell>
                    </TableRow>
                ))}
            </tbody>
        </Table>
    </>
);

const EnumTypeRenderer = ({ enumType }: { enumType: SerializedEnumType }) => (
    <>
        <HeadingWrapper>
            <Heading size="small" as="h3">
                {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
                {/* @ts-ignore: react-scroll types are not up to date with the latest react version*/}
                <Element id={enumType.name}>{enumType.name}</Element>
            </Heading>
        </HeadingWrapper>
        {enumType.description ? (
            <p>{enumType.description}</p>
        ) : (
            <p>
                Enum type with the following values: <b>{enumType.values.join(', ')}</b>
            </p>
        )}
    </>
);

const UnionTypeRenderer = ({ unionType }: { unionType: SerializedUnionType }) => (
    <>
        <HeadingWrapper>
            <Heading size="small" as="h3">
                {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
                {/* @ts-ignore: react-scroll types are not up to date with the latest react version*/}
                <Element id={unionType.name}>{unionType.name}</Element>
            </Heading>
        </HeadingWrapper>
        {unionType.description ? (
            <p>{unionType.description}</p>
        ) : (
            <p>
                Union of the following types:{' '}
                {unionType.types.map((_type, i) => (
                    <React.Fragment key={`${_type}-link`}>
                        {i === 0 ? '' : ', '}
                        <LinkToElement to={_type} />
                    </React.Fragment>
                ))}
            </p>
        )}
    </>
);

const ScalarRenderer = ({ scalar }: { scalar: SerializedScalar }) => (
    <>
        <HeadingWrapper>
            <Heading size="small" as="h3">
                {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
                {/* @ts-ignore: react-scroll types are not up to date with the latest react version*/}
                <Element id={scalar.name}>{scalar.name}</Element>
            </Heading>
        </HeadingWrapper>
        <p>{scalar.description}</p>
    </>
);

const SchemasRenderer = ({ schemaDocuments: { schema, contextSchema } }: Omit<ExtensionPointInfoProps, 'name'>) => {
    const {
        objectTypes: { Schema: attributesDocument, ...attributeObjectTypes } = { Schema: null },
        scalars: attributeScalars,
        enumTypes: attributesEnumTypes,
        unionTypes: attributesUnionTypes,
    } = schema || {};

    const {
        objectTypes: { ContextSchema: contextDocument, ...contextObjectTypes } = { ContextSchema: null },
        scalars: contextScalars,
        enumTypes: contextEnumTypes,
        unionTypes: contextUnionTypes,
    } = contextSchema || {};

    const objectTypeEntries: SerializedObjectType[] = Object.values({ ...attributeObjectTypes, ...contextObjectTypes });
    const scalars: SerializedScalar[] = Object.values({ ...attributeScalars, ...contextScalars });
    const enumTypes: SerializedEnumType[] = Object.values({ ...attributesEnumTypes, ...contextEnumTypes });
    const unionTypes: SerializedUnionType[] = Object.values({ ...attributesUnionTypes, ...contextUnionTypes });

    return (
        <>
            <Heading size="medium" as="h2">
                Attributes
            </Heading>
            {attributesDocument ? (
                <>
                    <p>List of attributes supported by this extension point.</p>
                    <TypeRenderer objectType={attributesDocument} />
                </>
            ) : (
                <p>No attributes information provided for this extension point.</p>
            )}

            <HeadingWrapper>
                <Heading size="medium" as="h2">
                    Context
                </Heading>
            </HeadingWrapper>
            {contextDocument ? (
                <>
                    <p>List of values provided by this extension point as context.</p>
                    <TypeRenderer objectType={contextDocument} />
                </>
            ) : (
                <p>No context information provided for this extension point.</p>
            )}

            <HeadingWrapper>
                <Heading size="medium" as="h2">
                    Types reference
                </Heading>
            </HeadingWrapper>
            {objectTypeEntries &&
                objectTypeEntries.map((objectType) => (
                    <React.Fragment key={objectType.name}>
                        <HeadingWrapper>
                            <Heading size="small" as="h3">
                                {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
                                {/* @ts-ignore: react-scroll types are not up to date with the latest react version*/}
                                <Element id={objectType.name}>{objectType.name}</Element>
                            </Heading>
                        </HeadingWrapper>
                        <TypeRenderer objectType={objectType} />
                    </React.Fragment>
                ))}

            {enumTypes && enumTypes.map((enumType) => <EnumTypeRenderer key={enumType.name} enumType={enumType} />)}

            {unionTypes && unionTypes.map((unionType) => <UnionTypeRenderer key={unionType.name} unionType={unionType} />)}

            {scalars && scalars.map((scalar) => <ScalarRenderer key={scalar.name} scalar={scalar} />)}
        </>
    );
};

const bodyStyles: React.CSSProperties = {
    // Styles required for the react-scroll to work when scrolling is animated
    overflowY: 'auto',
    overflowX: 'hidden',
    // The rest of the styles are copied from the @atlaskit/modal-dialog/dist/esm/modal-body.js
    padding: GRID_SIZE * 3,
    flex: '1 1 auto',
};

const CustomModalBody: React.FC<{ children: React.ReactNode }> = ({ children }) => {
    return (
        <div id="___cse_info_container" style={bodyStyles}>
            {children}
        </div>
    );
};

const ExtensionPointInfo: React.FC<ExtensionPointInfoProps> = ({ name, schemaDocuments }) => {
    const [showExtensionPointInfo] = useDiscovery();
    const [dialogOpen, setDialogState] = useState(false);

    const closeDialog = () => setDialogState(false);
    return !showExtensionPointInfo ? null : (
        <BlinkerContainer>
            <Blinker type="button" onClick={() => setDialogState(true)} />
            <ModalTransition>
                {dialogOpen && (
                    <Modal onClose={closeDialog} width="x-large">
                        <ModalHeader hasCloseButton>
                            <ModalTitle>{`Extension point: ${name}`}</ModalTitle>
                        </ModalHeader>

                        <CustomModalBody>
                            <SchemasRenderer schemaDocuments={schemaDocuments} />
                        </CustomModalBody>

                        <ModalFooter>
                            {/* eslint-disable-next-line jsx-a11y/no-autofocus */}
                            <Button appearance="primary" autoFocus onClick={closeDialog}>
                                Close
                            </Button>
                        </ModalFooter>
                    </Modal>
                )}
            </ModalTransition>
        </BlinkerContainer>
    );
};

export default memo(ExtensionPointInfo);
