import React from 'react';
import CDN_MANIFEST from '../../../assets/manifest.json';
import { CdnIcon } from '../cdnIcon';
import { CdnAssetError } from './cdnAssetError';

export type Manifest = typeof CDN_MANIFEST;
export type VariantType<T> = T extends string ? never : keyof T;
export { CDN_MANIFEST };

export interface CdnAssetProps<
    Category extends keyof Manifest,
    Name extends keyof Manifest[Category],
    Size extends keyof Manifest[Category][Name],
    Variant extends VariantType<Manifest[Category][Name][Size]>,
> {
    category: Category;
    name: Name;
    variant?: Variant;
    size?: Size;
    alt?: string;
    className?: string;
    testId?: string;
    width?: string | number;
    height?: string | number;
    cdnUrl?: string;
    title?: string;
}

export function CdnAsset<
    C extends keyof Manifest,
    N extends keyof Manifest[C],
    S extends keyof Manifest[C][N],
    V extends VariantType<Manifest[C][N][S]>,
>({
    name,
    category,
    size,
    variant,
    alt,
    className,
    testId,
    width,
    height,
    cdnUrl,
    title,
}: CdnAssetProps<C, N, S, V>) {
    if (!category) throw new Error('Missing required "category" property');
    if (!name) throw new Error('Missing required "name" property');

    const fileNames = CDN_MANIFEST[category];
    if (!fileNames) throw new CdnAssetError('category', [category], CDN_MANIFEST);

    const fileSizes = fileNames[name];
    if (!fileSizes) throw new CdnAssetError('name', [category, name], fileNames);

    const sizePx = size ? ((typeof size === 'number' ? `${size}` : size) as S) : undefined;
    const fileSize = sizePx ?? (Object.keys(fileSizes)[0] as S);
    const fileVariantsOrPath = fileSizes[fileSize];
    if (!fileVariantsOrPath) throw new CdnAssetError('size', [category, name, fileSize], fileSizes);

    let filePath;
    if (typeof fileVariantsOrPath === 'string') filePath = fileVariantsOrPath;
    else if (variant) {
        filePath = fileVariantsOrPath[variant];

        if (!filePath)
            throw new CdnAssetError(
                'variant',
                [category, name, fileSize, variant],
                fileVariantsOrPath
            );
    }

    if (!filePath && typeof fileVariantsOrPath === 'object') {
        filePath = fileVariantsOrPath['default' as V];
        if (!filePath) filePath = fileVariantsOrPath[Object.keys(fileVariantsOrPath)[0] as V];
    }

    if (!filePath) throw new CdnAssetError('path', [category, name, fileSize]);

    const assetSize = parseInt(fileSize.toString(), 10);
    const assetSizeControl = !isNaN(assetSize) ? assetSize : undefined;

    return (
        <CdnIcon
            testId={testId}
            className={className}
            alt={alt ?? `${category}/${name.toString()}`}
            width={width ?? assetSizeControl}
            height={height ?? assetSizeControl}
            folder={category}
            cdnUrl={cdnUrl}
            title={title}
            filePath={filePath.toString()}
        />
    );
}
