import React, { useEffect, useMemo, useRef } from 'react';
import { useTypedSelector } from 'store';
import CloudIcon from 'assets/icons/cloud.svg';
import CloudDownArrow from 'assets/icons/cloud-down-arrow.svg';
import CloudExclamationMarkIcon from 'assets/icons/cloud-exclamation-mark.svg';
import InternalDriveIcon from 'assets/icons/internal-drive.svg';
import useDefaultStyles from './Colors';
import { EntityId } from '@reduxjs/toolkit';
import Svg, { Circle, CircleProps } from 'react-native-svg';
import { Animated, Easing } from 'react-native';
import styled from 'styled-components/native';

interface DownloadIconProps {
    trackId: EntityId;
    size?: number;
    fill?: string;
}

const DownloadContainer = styled.View`
    position: relative;
`;

const IconOverlay = styled.View`
    position: absolute;
    top: 0;
    left: 0;
    transform: scale(0.5);
`;

function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
    // determine styles
    const defaultStyles = useDefaultStyles();
    const iconFill = fill || defaultStyles.textQuarterOpacity.color;

    // Get download icon from state
    const entity = useTypedSelector((state) => state.downloads.entities[trackId]);
    const isQueued = useTypedSelector((state) => state.downloads.queued.includes(trackId));

    // Memoize calculations for radius and circumference of the circle
    const radius = useMemo(() => size / 2, [size]);
    const circumference = useMemo(() => radius * 2 * Math.PI, [radius]);

    // Initialize refs for the circle and the animated value
    const circleRef = useRef<Circle>(null);
    const offsetAnimation = useRef(new Animated.Value(entity?.progress || 0)).current;

    // Whenever the progress changes, trigger the animation
    useEffect(() => {
        Animated.timing(offsetAnimation, {
            toValue: (circumference * (1 - (entity?.progress || 0))),
            duration: 250,
            useNativeDriver: false,
            easing: Easing.ease,
        }).start();
    }, [entity?.progress, offsetAnimation, circumference]);

    // On mount, subscribe to changes in the animation value and then 
    // apply them to the circle using native props
    useEffect(() => {
        const subscription = offsetAnimation.addListener((offset) => {
            // @ts-expect-error undocumented functionality
            const setNativeProps = circleRef.current?.setNativeProps as (props: CircleProps) => void | undefined;
            setNativeProps?.({ strokeDashoffset: offset.value });
        });

        return () => offsetAnimation.removeListener(subscription);
    }, [offsetAnimation]);

    if (!entity && !isQueued) {
        return (
            <CloudIcon width={size} height={size} fill={iconFill} />
        );
    }

    if (entity?.isComplete) {
        return (
            <InternalDriveIcon width={size} height={size} fill={iconFill} />
        );
    }

    if (entity?.isFailed) {
        return (
            <CloudExclamationMarkIcon width={size} height={size} fill={iconFill} />
        );
    }

    if (isQueued || (!entity?.isFailed && !entity?.isComplete)) {
        return (
            <DownloadContainer>
                <Svg width={size} height={size} transform={[{ rotate: '-90deg' }]}>
                    <Circle
                        cx={radius}
                        cy={radius}
                        r={radius - 1}
                        stroke={iconFill}
                        // @ts-expect-error react-native-svg has outdated react-native typings
                        ref={circleRef}
                        strokeWidth={1.5}
                        strokeDasharray={[ circumference, circumference ]}
                        strokeDashoffset={circumference}
                        strokeLinecap='round'
                        fill='transparent'
                    />
                </Svg>
                <IconOverlay>
                    <CloudDownArrow width={size} height={size} fill={iconFill} />
                </IconOverlay>
            </DownloadContainer>
        );
    }

    return null;
}

export default DownloadIcon;