import * as React from 'react'
import type { ForwardedRef, PropsWithoutRef, RefAttributes } from 'react'
import styled, { css } from 'styled-components'
import { createSVGGradient, generateGradientId } from './gradient'

const IconWrap = styled.div<{ $gradientId?: string }>`
    display: contents;
    ${(props) =>
        props.$gradientId
            ? css`
                  svg path {
                      fill: url(#${props.$gradientId});
                  }
              `
            : ''}
`

const Container = styled.div<{ $h: number; $w: number }>`
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    height: ${(props) => props.$h}px;
    min-height: ${(props) => props.$h}px;
    width: ${(props) => props.$w}px;
`

const HiddenSVG = styled.svg`
    display: block;
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
`

const DEFAULT_SIZE = {
    wrapper: 36, // @planview/pv-utilities/size/small
    svg: 16, // @planview/pv-utilities/icon-size/medium
}

type IconAndContainerSize = {
    svg: number
    wrapper: number
}

export type IconProps = React.SVGAttributes<SVGSVGElement> &
    React.HTMLAttributes<HTMLDivElement> & {
        /**
         * Color can be a string color or one of the predefined gradients `anvi` or `anvi-inverse`
         */
        color?: 'anvi' | 'anvi-inverse' | (string & {})
        /**
         * Size can be a number (applies to both icon and container) or an object to define different sizes for icon and container
         */
        size?: number | IconAndContainerSize
    }

export function withIconWrapper(
    SvgIcon: React.ForwardRefExoticComponent<
        PropsWithoutRef<React.SVGProps<SVGSVGElement>> &
            RefAttributes<SVGSVGElement>
    >,
    name: string
) {
    const GenIcon: React.ForwardRefRenderFunction<
        SVGSVGElement | HTMLDivElement,
        IconProps
    > = ({ color, size = DEFAULT_SIZE, ...props }, ref) => {
        const dataTestId = `svg-icon-${name}`.toLowerCase()
        const gradientId = React.useMemo(
            () =>
                color === 'anvi' || color === 'anvi-inverse'
                    ? generateGradientId(color)
                    : undefined,
            [color]
        )
        const gradientType =
            color === 'anvi' || color === 'anvi-inverse' ? color : null
        let comp = null
        if (typeof size === 'number') {
            comp = (
                <SvgIcon
                    {...props}
                    data-testid={dataTestId}
                    aria-hidden="true"
                    height={size}
                    width={size}
                    color={color}
                    ref={ref as ForwardedRef<SVGSVGElement>}
                />
            )
        } else {
            const { svg, wrapper } = size
            comp = (
                <Container
                    {...props}
                    $h={wrapper}
                    $w={wrapper}
                    ref={ref as ForwardedRef<HTMLDivElement>}
                >
                    <SvgIcon
                        data-testid={dataTestId}
                        aria-hidden="true"
                        height={svg}
                        width={svg}
                        color={color}
                    />
                </Container>
            )
        }

        return gradientId && gradientType ? (
            <IconWrap $gradientId={gradientId}>
                <HiddenSVG aria-hidden="true">
                    <defs>{createSVGGradient(gradientType, gradientId)}</defs>
                </HiddenSVG>
                {comp}
            </IconWrap>
        ) : (
            comp
        )
    }
    GenIcon.displayName = name
    return React.forwardRef(GenIcon)
}
