import { DownloadStatus, useAttachment } from "@/app/providers/AttachmentProvider/useAttachment";
import { AttachmentProps } from "./MessageAttachments";
import { Avatar, Box, Flex, Text, useMantineTheme } from "@mantine/core";
import { AnimatedRoundedProgress } from "../AnimatedRoundedProgress/AnimatedRoundedProgress";
import { IconArrowDown, IconPlayerPauseFilled, IconPlayerPlayFilled, IconX } from "@tabler/icons-react";
import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider";
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
import { useMemo, useRef } from "react";
import { usePlayerContext } from "@/app/providers/PlayerProvider/usePlayerContext";

const WAVE_BARS = 40;
const BAR_WIDTH = 2;
const BAR_GAP = 2;
const MIN_BAR_HEIGHT = 4;
const MAX_BAR_HEIGHT = 24;

function normalizeWaves(source: number[], targetLength: number): number[] {
    if (targetLength <= 0) return [];
    if (source.length === 0) return Array(targetLength).fill(0);
    if (source.length === targetLength) return source;

    if (source.length > targetLength) {
        const compressed: number[] = [];
        const bucketSize = source.length / targetLength;

        for (let i = 0; i < targetLength; i++) {
            const start = Math.floor(i * bucketSize);
            const end = Math.max(start + 1, Math.floor((i + 1) * bucketSize));

            let max = 0;
            for (let j = start; j < end && j < source.length; j++) {
                if (source[j] > max) max = source[j];
            }

            compressed.push(max);
        }

        return compressed;
    }

    if (targetLength === 1) return [source[0]];

    const stretched: number[] = [];
    const lastSourceIndex = source.length - 1;

    for (let i = 0; i < targetLength; i++) {
        const position = (i * lastSourceIndex) / (targetLength - 1);
        const left = Math.floor(position);
        const right = Math.min(Math.ceil(position), lastSourceIndex);

        if (left === right) {
            stretched.push(source[left]);
            continue;
        }

        const t = position - left;
        stretched.push(source[left] * (1 - t) + source[right] * t);
    }

    return stretched;
}

function formatTime(seconds: number) {
    const s = Math.max(0, Math.floor(seconds));
    const m = Math.floor(s / 60).toString().padStart(2, "0");
    const r = (s % 60).toString().padStart(2, "0");
    return `${m}:${r}`;
}

export function MessageVoice(props: AttachmentProps) {
    const { downloadPercentage, downloadStatus, uploadedPercentage, download, getPreview } = useAttachment(
        props.attachment,
        props.parent,
    );

    const theme = useMantineTheme();
    const colors = useRosettaColors();

    const preview = getPreview() || "";
    const [durationPart = "0", wavesPart = ""] = preview.split("::");
    const previewDuration = Number.parseInt(durationPart, 10) || 0;

    const rawWaves = useMemo(
        () =>
            wavesPart
                .split(",")
                .map((s) => Number.parseFloat(s))
                .filter((n) => Number.isFinite(n) && n >= 0),
        [wavesPart]
    );

    const waves = useMemo(() => normalizeWaves(rawWaves, WAVE_BARS), [rawWaves]);

    const peak = useMemo(() => {
        const max = Math.max(...waves, 0);
        return max > 0 ? max : 1;
    }, [waves]);

    const isUploading =
        props.delivered === DeliveredMessageState.WAITING &&
        uploadedPercentage > 0 &&
        uploadedPercentage < 100;
    const error = downloadStatus === DownloadStatus.ERROR;

    const waveformWidth = WAVE_BARS * BAR_WIDTH + (WAVE_BARS - 1) * BAR_GAP;
    const waveformRef = useRef<HTMLDivElement | null>(null);

    const {
        playAudio,
        pause,
        duration: currentDuration,
        playing,
        setDuration,
        totalDuration,
        currentMessageId,
    } = usePlayerContext();
    const messageId = String((props.parent as any)?.id ?? (props.attachment as any)?.messageId ?? props.attachment.id);
    const isCurrentTrack = currentMessageId === messageId;

    const fullDuration = Math.max(isCurrentTrack && totalDuration > 0 ? totalDuration : previewDuration, 1);
    const safeCurrent = isCurrentTrack ? currentDuration : 0;
    const playbackProgress = Math.max(0, Math.min(1, safeCurrent / fullDuration));

    const createAudioBlob = () => new Blob([Buffer.from(props.attachment.blob, "binary")], { type: "audio/webm;codecs=opus" });

    const ensureStarted = (seekToSec?: number) => {
        const blob = createAudioBlob();
        playAudio("Voice Message", "", blob, messageId);

        if (typeof seekToSec === "number") {
            requestAnimationFrame(() => setDuration(seekToSec));
        }
    };

    const handleMainAction = () => {
        if (error) return;

        if (downloadStatus !== DownloadStatus.DOWNLOADED) {
            download();
            return;
        }

        if (!isCurrentTrack) {
            ensureStarted();
            return;
        }

        if (playing) {
            pause();
            return;
        }

        ensureStarted(Math.max(0, safeCurrent));
    };

    const handleSeek = (e: React.MouseEvent<HTMLDivElement>) => {
        if (error || downloadStatus !== DownloadStatus.DOWNLOADED) return;

        const rect = waveformRef.current?.getBoundingClientRect();
        if (!rect || rect.width <= 0) return;

        const x = e.clientX - rect.left;
        const progress = Math.max(0, Math.min(1, x / rect.width));
        const seekTo = progress * fullDuration;

        if (!isCurrentTrack) {
            ensureStarted(seekTo);
            return;
        }

        setDuration(seekTo);
    };

    const timeText =
        isCurrentTrack && safeCurrent > 0
            ? `-${formatTime(Math.max(0, fullDuration - safeCurrent))}`
            : formatTime(fullDuration);

    return (
        <Flex gap="sm" align="center">
            <Avatar
                bg={error ? colors.error : colors.brandColor}
                size={40}
                style={{ cursor: "pointer", position: "relative" }}
                onClick={handleMainAction}
            >
                {!error && (
                    <>
                        {downloadStatus === DownloadStatus.DOWNLOADING &&
                            downloadPercentage > 0 &&
                            downloadPercentage < 100 && (
                                <div style={{ position: "absolute", top: 0, left: 0 }}>
                                    <AnimatedRoundedProgress size={40} value={downloadPercentage} />
                                </div>
                            )}

                        {isUploading && (
                            <div style={{ position: "absolute", top: 0, left: 0 }}>
                                <AnimatedRoundedProgress color="#fff" size={40} value={uploadedPercentage} />
                            </div>
                        )}

                        {downloadStatus !== DownloadStatus.DOWNLOADED && <IconArrowDown color="white" size={22} />}

                        {downloadStatus === DownloadStatus.DOWNLOADED && !isUploading &&
                            (isCurrentTrack && playing ? (
                                <IconPlayerPauseFilled color="white" size={22} />
                            ) : (
                                <IconPlayerPlayFilled color="white" size={22} />
                            ))}
                    </>
                )}

                {(error || isUploading) && <IconX color="white" size={22} />}
            </Avatar>

            <Flex direction="column">
                <Box
                    ref={waveformRef}
                    w={waveformWidth}
                    h={32}
                    onClick={handleSeek}
                    style={{ overflow: "hidden", cursor: "pointer" }}
                >
                    <Flex h="100%" align="center" gap={BAR_GAP} wrap="nowrap">
                        {waves.map((value, index) => {
                            const normalized = Math.max(0, Math.min(1, value / peak));
                            const height = Math.max(
                                MIN_BAR_HEIGHT,
                                Math.min(MAX_BAR_HEIGHT, MIN_BAR_HEIGHT + normalized * (MAX_BAR_HEIGHT - MIN_BAR_HEIGHT))
                            );

                            const passed = playbackProgress * waves.length - index;
                            const fillPercent = Math.max(0, Math.min(1, passed));

                            const inactiveColor = theme.colors.gray[4];
                            const activeColor = colors.brandColor;

                            let background = inactiveColor;
                            if (fillPercent >= 1) {
                                background = activeColor;
                            } else if (fillPercent > 0) {
                                background = `linear-gradient(90deg, ${activeColor} 0%, ${activeColor} ${fillPercent * 100}%, ${inactiveColor} ${fillPercent * 100}%, ${inactiveColor} 100%)`;
                            }

                            return (
                                <Box
                                    key={index}
                                    w={BAR_WIDTH}
                                    h={height}
                                    style={{
                                        flex: `0 0 ${BAR_WIDTH}px`,
                                        borderRadius: 999,
                                        background,
                                    }}
                                />
                            );
                        })}
                    </Flex>
                </Box>

                <Text size="xs" c="dimmed">
                    {timeText}
                </Text>
            </Flex>
        </Flex>
    );
}