package com.fieldlabel.collector.ui.screens

import android.content.Context
import android.media.AudioAttributes
import android.media.MediaCodec
import android.media.MediaExtractor
import android.media.MediaFormat
import android.media.MediaPlayer
import android.net.Uri
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackParameters
import androidx.media3.exoplayer.ExoPlayer
import coil.compose.AsyncImage
import com.fieldlabel.collector.ui.viewmodel.MediaPreviewViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.io.File
import java.nio.ByteBuffer
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

// ─────────────────────────────────────────────────────────────────────────────
// MediaPreviewScreen — 写真・動画・音声のフルスクリーンプレビュー
// ─────────────────────────────────────────────────────────────────────────────

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediaPreviewScreen(
    filePath: String,
    mediaType: String,
    onBack: () -> Unit,
    viewModel: MediaPreviewViewModel = hiltViewModel()
) {
    val locationRecord by viewModel.locationRecord.collectAsState()
    val specimenId = viewModel.specimenId.ifBlank { null }

    val title = when (mediaType) {
        "video" -> "動画プレビュー"
        "audio" -> "音声プレビュー"
        else    -> "写真プレビュー"
    }

    // オーバーレイの表示/非表示トグル
    var overlayVisible by remember { mutableStateOf(true) }

    // 地点情報コンテンツ（写真オーバーレイ・動画/音声パネル共用）
    val infoContent: @Composable () -> Unit = {
        MediaInfoOverlay(
            locationId     = viewModel.locationId.ifBlank { null },
            specimenId     = specimenId,
            address        = locationRecord?.address,
            collectionDate = locationRecord?.collectionDate?.toString(),
            collectorName  = locationRecord?.collectorName
        )
    }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(title) },
                navigationIcon = {
                    IconButton(onClick = onBack) {
                        Icon(Icons.Default.ArrowBack, "戻る")
                    }
                },
                actions = {
                    // 情報表示切替ボタン
                    IconButton(onClick = { overlayVisible = !overlayVisible }) {
                        Icon(
                            Icons.Default.Info,
                            contentDescription = "情報を${if (overlayVisible) "隠す" else "表示"}",
                            tint = if (overlayVisible) Color.White else Color.White.copy(alpha = 0.4f)
                        )
                    }
                },
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor = Color.Black.copy(alpha = 0.7f),
                    titleContentColor = Color.White,
                    navigationIconContentColor = Color.White,
                    actionIconContentColor = Color.White
                )
            )
        },
        containerColor = Color.Black
    ) { padding ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(padding)
        ) {
            when (mediaType) {
                "video" -> VideoPlayer(
                    filePath    = filePath,
                    infoContent = if (overlayVisible) infoContent else null
                )
                "audio" -> AudioPlayer(
                    filePath    = filePath,
                    infoContent = if (overlayVisible) infoContent else null
                )
                else    -> {
                    // 写真: メディアを中央に配置し、情報は BottomStart オーバーレイで表示
                    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                        ZoomablePhoto(filePath = filePath)
                    }
                    AnimatedVisibility(
                        visible  = overlayVisible,
                        enter    = fadeIn(),
                        exit     = fadeOut(),
                        modifier = Modifier.align(Alignment.BottomStart)
                    ) {
                        infoContent()
                    }
                }
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 情報オーバーレイ — 画面下端に半透明カードとして表示
// ─────────────────────────────────────────────────────────────────────────────

@Composable
private fun MediaInfoOverlay(
    locationId:     String?,
    specimenId:     String?,
    address:        String?,
    collectionDate: String?,
    collectorName:  String?
) {
    if (locationId.isNullOrBlank()) return   // 情報なし → 非表示

    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 0.dp, vertical = 4.dp),
        shape  = RoundedCornerShape(8.dp),
        color  = Color.Black.copy(alpha = 0.55f)
    ) {
        Column(
            modifier            = Modifier.padding(horizontal = 14.dp, vertical = 10.dp),
            verticalArrangement = Arrangement.spacedBy(4.dp)
        ) {
            // 標本 ID or 地点 ID（大きめに）
            val primaryLabel = if (specimenId != null) specimenId else locationId
            val primaryIcon  = if (specimenId != null) Icons.Default.Eco else Icons.Default.Place
            Row(
                verticalAlignment     = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(6.dp)
            ) {
                Icon(
                    primaryIcon, null,
                    tint     = Color.White,
                    modifier = Modifier.size(16.dp)
                )
                Text(
                    text       = primaryLabel ?: "",
                    fontSize   = 15.sp,
                    fontWeight = FontWeight.Bold,
                    fontFamily = FontFamily.Monospace,
                    color      = Color.White,
                    maxLines   = 1,
                    overflow   = TextOverflow.Ellipsis
                )
            }

            // 地点 ID（標本付きの場合に補足表示）
            if (specimenId != null && locationId != null) {
                Row(
                    verticalAlignment     = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(6.dp)
                ) {
                    Icon(
                        Icons.Default.Place, null,
                        tint     = Color.White.copy(alpha = 0.7f),
                        modifier = Modifier.size(13.dp)
                    )
                    Text(
                        text     = locationId,
                        fontSize = 12.sp,
                        fontFamily = FontFamily.Monospace,
                        color    = Color.White.copy(alpha = 0.7f),
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }

            // 住所
            if (!address.isNullOrBlank()) {
                Row(
                    verticalAlignment     = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(6.dp)
                ) {
                    Icon(
                        Icons.Default.LocationOn, null,
                        tint     = Color.White.copy(alpha = 0.7f),
                        modifier = Modifier.size(13.dp)
                    )
                    Text(
                        text     = address,
                        fontSize = 12.sp,
                        color    = Color.White.copy(alpha = 0.85f),
                        maxLines = 2,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }

            // 採集日・採集者
            if (!collectionDate.isNullOrBlank() || !collectorName.isNullOrBlank()) {
                Row(
                    verticalAlignment     = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(6.dp)
                ) {
                    Icon(
                        Icons.Default.CalendarToday, null,
                        tint     = Color.White.copy(alpha = 0.7f),
                        modifier = Modifier.size(13.dp)
                    )
                    val dateCollector = listOfNotNull(collectionDate, collectorName).joinToString("  ")
                    Text(
                        text     = dateCollector,
                        fontSize = 12.sp,
                        color    = Color.White.copy(alpha = 0.75f),
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// ① 写真 — ピンチズーム・パン対応
// ─────────────────────────────────────────────────────────────────────────────

@Composable
private fun ZoomablePhoto(filePath: String) {
    var scale by remember { mutableStateOf(1f) }
    var offsetX by remember { mutableStateOf(0f) }
    var offsetY by remember { mutableStateOf(0f) }

    AsyncImage(
        model = File(filePath),
        contentDescription = "写真",
        contentScale = ContentScale.Fit,
        modifier = Modifier
            .fillMaxSize()
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale,
                translationX = offsetX,
                translationY = offsetY
            )
            .pointerInput(Unit) {
                detectTransformGestures { _, pan, zoom, _ ->
                    val newScale = (scale * zoom).coerceIn(0.5f, 8f)
                    scale = newScale
                    // パン量をスケールに比例させる
                    offsetX += pan.x * newScale
                    offsetY += pan.y * newScale
                }
            }
    )

    // ダブルタップリセットボタン
    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
        if (scale != 1f || offsetX != 0f || offsetY != 0f) {
            TextButton(
                onClick = { scale = 1f; offsetX = 0f; offsetY = 0f },
                modifier = Modifier.padding(bottom = 24.dp),
                colors = ButtonDefaults.textButtonColors(contentColor = Color.White)
            ) {
                Icon(Icons.Default.ZoomOut, null, modifier = Modifier.size(16.dp))
                Spacer(Modifier.width(4.dp))
                Text("リセット")
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// ② 動画 — ExoPlayer（速度制御・コマ送り・シーク）
// ─────────────────────────────────────────────────────────────────────────────

private val VIDEO_SPEEDS = listOf(0.25f, 0.5f, 1.0f, 1.5f, 2.0f, 4.0f)
private const val FRAME_STEP_MS = 33L   // ~30fps ≒ 33ms/フレーム

@Composable
private fun VideoPlayer(
    filePath:    String,
    infoContent: (@Composable () -> Unit)? = null
) {
    val context = LocalContext.current
    val scope   = rememberCoroutineScope()

    // ExoPlayer インスタンス（Composable ライフサイクルに合わせて管理）
    val player = remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(Uri.fromFile(File(filePath))))
            prepare()
            playWhenReady = true
        }
    }

    // PlayerView の参照を保持し、dispose 時に先に player を切り離すことで
    // 画面遷移時に動画が一瞬遅れて消える問題を防ぐ
    var playerViewRef by remember { mutableStateOf<androidx.media3.ui.PlayerView?>(null) }
    DisposableEffect(Unit) {
        onDispose {
            playerViewRef?.player = null   // 先にサーフェスから切り離す
            player.stop()
            player.release()
        }
    }

    var isPlaying     by remember { mutableStateOf(true) }
    var speedIdx      by remember { mutableStateOf(2) }          // index into VIDEO_SPEEDS (1.0x)
    var positionMs    by remember { mutableStateOf(0L) }
    var durationMs    by remember { mutableStateOf(1L) }
    var seeking       by remember { mutableStateOf(false) }

    // 再生位置を定期更新
    LaunchedEffect(player) {
        while (isActive) {
            if (!seeking) {
                positionMs = player.currentPosition.coerceAtLeast(0L)
                durationMs = player.duration.coerceAtLeast(1L)
                isPlaying  = player.isPlaying
            }
            delay(200)
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black),
        verticalArrangement = Arrangement.SpaceBetween
    ) {
        // ── 動画サーフェス ──
        AndroidView(
            factory = { ctx ->
                androidx.media3.ui.PlayerView(ctx).apply {
                    this.player = player
                    useController = false   // カスタムコントローラーを使う
                    playerViewRef = this
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
        )

        // ── コントロールパネル ──
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color.Black.copy(alpha = 0.8f))
                .padding(horizontal = 16.dp, vertical = 8.dp),
            verticalArrangement = Arrangement.spacedBy(4.dp)
        ) {
            // シークバー
            Slider(
                value = positionMs.toFloat(),
                valueRange = 0f..durationMs.toFloat(),
                onValueChange = { v ->
                    seeking = true
                    positionMs = v.toLong()
                },
                onValueChangeFinished = {
                    player.seekTo(positionMs)
                    seeking = false
                },
                colors = SliderDefaults.colors(
                    thumbColor = MaterialTheme.colorScheme.primary,
                    activeTrackColor = MaterialTheme.colorScheme.primary
                ),
                modifier = Modifier.fillMaxWidth()
            )

            // 時刻表示
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Text(formatTime(positionMs), color = Color.White, style = MaterialTheme.typography.labelSmall)
                Text(formatTime(durationMs), color = Color.Gray,  style = MaterialTheme.typography.labelSmall)
            }

            // 再生コントロール行
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceEvenly,
                verticalAlignment = Alignment.CenterVertically
            ) {
                // 後退10秒
                IconButton(onClick = { player.seekTo((player.currentPosition - 10_000).coerceAtLeast(0)) }) {
                    Icon(Icons.Default.Replay10, "10秒戻す", tint = Color.White)
                }

                // 前フレーム（コマ戻し）
                IconButton(onClick = {
                    player.pause()
                    player.seekTo((player.currentPosition - FRAME_STEP_MS).coerceAtLeast(0))
                    isPlaying = false
                }) {
                    Icon(Icons.Default.SkipPrevious, "前フレーム", tint = Color.White)
                }

                // 再生/一時停止
                FilledIconButton(
                    onClick = {
                        if (player.isPlaying) { player.pause(); isPlaying = false }
                        else { player.play(); isPlaying = true }
                    },
                    modifier = Modifier.size(52.dp)
                ) {
                    Icon(
                        if (isPlaying) Icons.Default.Pause else Icons.Default.PlayArrow,
                        if (isPlaying) "一時停止" else "再生",
                        modifier = Modifier.size(28.dp)
                    )
                }

                // 次フレーム（コマ送り）
                IconButton(onClick = {
                    player.pause()
                    player.seekTo((player.currentPosition + FRAME_STEP_MS).coerceAtMost(player.duration))
                    isPlaying = false
                }) {
                    Icon(Icons.Default.SkipNext, "次フレーム", tint = Color.White)
                }

                // 早送り10秒
                IconButton(onClick = { player.seekTo((player.currentPosition + 10_000).coerceAtMost(player.duration)) }) {
                    Icon(Icons.Default.Forward10, "10秒送る", tint = Color.White)
                }
            }

            // 再生速度ボタン
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text("速度:", color = Color.Gray, style = MaterialTheme.typography.labelSmall,
                    modifier = Modifier.padding(end = 8.dp))
                VIDEO_SPEEDS.forEachIndexed { idx, speed ->
                    val selected = speedIdx == idx
                    TextButton(
                        onClick = {
                            speedIdx = idx
                            player.playbackParameters = PlaybackParameters(speed)
                        },
                        modifier = Modifier
                            .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp)
                            .padding(horizontal = 2.dp),
                        contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
                        colors = ButtonDefaults.textButtonColors(
                            contentColor = if (selected) MaterialTheme.colorScheme.primary else Color.Gray
                        )
                    ) {
                        Text(
                            "${if (speed < 1f) speed else speed.toInt()}×",
                            style = MaterialTheme.typography.labelMedium
                        )
                    }
                }
            }

            // ── 地点情報パネル（コントロール下部に表示） ──
            if (infoContent != null) {
                infoContent()
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// ③ 音声 — MediaPlayer + シークバー + 波形表示
// ─────────────────────────────────────────────────────────────────────────────

@Composable
private fun AudioPlayer(
    filePath:    String,
    infoContent: (@Composable () -> Unit)? = null
) {
    val context = LocalContext.current
    val scope   = rememberCoroutineScope()

    val player = remember {
        MediaPlayer().apply {
            setAudioAttributes(
                AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .build()
            )
            setDataSource(filePath)
            prepare()
        }
    }
    DisposableEffect(Unit) {
        onDispose { player.stop(); player.release() }
    }

    var isPlaying     by remember { mutableStateOf(false) }
    var positionMs    by remember { mutableStateOf(0) }
    val durationMs    = remember { player.duration.coerceAtLeast(1) }
    var seeking       by remember { mutableStateOf(false) }
    var playbackSpeed by remember { mutableStateOf(1.0f) }
    val waveform      = remember { mutableStateOf<FloatArray?>(null) }

    // 波形データを非同期デコード
    LaunchedEffect(filePath) {
        waveform.value = decodeWaveform(context, filePath)
    }

    // 再生位置を定期更新
    LaunchedEffect(player) {
        while (isActive) {
            if (!seeking && player.isPlaying) {
                positionMs = player.currentPosition
                isPlaying  = true
            } else if (!player.isPlaying) {
                isPlaying = false
            }
            delay(50)
        }
    }

    // 再生終了時にリセット
    player.setOnCompletionListener {
        isPlaying  = false
        positionMs = 0
        player.seekTo(0)
    }

    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(modifier = Modifier.height(24.dp))

        // 音声アイコン
        Icon(
            Icons.Default.Audiotrack,
            contentDescription = null,
            modifier = Modifier.size(80.dp),
            tint = MaterialTheme.colorScheme.primary
        )

        Text(
            File(filePath).name,
            color = Color.White,
            style = MaterialTheme.typography.bodyMedium,
            textAlign = TextAlign.Center
        )

        // ── 波形表示 ──
        WaveformDisplay(
            waveform = waveform.value,
            progress = positionMs.toFloat() / durationMs.toFloat(),
            modifier = Modifier
                .fillMaxWidth()
                .height(96.dp)
        )

        // シークバー
        Slider(
            value = positionMs.toFloat(),
            valueRange = 0f..durationMs.toFloat(),
            onValueChange = { v ->
                seeking = true
                positionMs = v.toInt()
            },
            onValueChangeFinished = {
                player.seekTo(positionMs)
                seeking = false
            },
            colors = SliderDefaults.colors(
                thumbColor = MaterialTheme.colorScheme.primary,
                activeTrackColor = MaterialTheme.colorScheme.primary
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
            Text(formatTime(positionMs.toLong()), color = Color.White, style = MaterialTheme.typography.labelSmall)
            Text(formatTime(durationMs.toLong()), color = Color.Gray,  style = MaterialTheme.typography.labelSmall)
        }

        // 再生コントロール
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            // 10秒戻す
            IconButton(onClick = {
                val newPos = (player.currentPosition - 10_000).coerceAtLeast(0)
                player.seekTo(newPos); positionMs = newPos
            }) {
                Icon(Icons.Default.Replay10, "10秒戻す", tint = Color.White)
            }

            // 再生/一時停止
            FilledIconButton(
                onClick = {
                    if (player.isPlaying) { player.pause(); isPlaying = false }
                    else { player.start(); isPlaying = true }
                },
                modifier = Modifier.size(64.dp)
            ) {
                Icon(
                    if (isPlaying) Icons.Default.Pause else Icons.Default.PlayArrow,
                    if (isPlaying) "一時停止" else "再生",
                    modifier = Modifier.size(32.dp)
                )
            }

            // 10秒送る
            IconButton(onClick = {
                val newPos = (player.currentPosition + 10_000).coerceAtMost(durationMs)
                player.seekTo(newPos); positionMs = newPos
            }) {
                Icon(Icons.Default.Forward10, "10秒送る", tint = Color.White)
            }
        }

        // 再生速度
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text("速度:", color = Color.Gray, style = MaterialTheme.typography.labelSmall,
                modifier = Modifier.padding(end = 8.dp))
            listOf(0.5f, 1.0f, 1.5f, 2.0f).forEach { speed ->
                val selected = playbackSpeed == speed
                TextButton(
                    onClick = {
                        playbackSpeed = speed
                        try {
                            val params = android.media.PlaybackParams().setSpeed(speed)
                            player.playbackParams = params
                        } catch (_: Exception) {}
                    },
                    contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
                    colors = ButtonDefaults.textButtonColors(
                        contentColor = if (selected) MaterialTheme.colorScheme.primary else Color.Gray
                    )
                ) {
                    Text(
                        "${if (speed == speed.toInt().toFloat()) speed.toInt() else speed}×",
                        style = MaterialTheme.typography.labelMedium
                    )
                }
            }
        }

        // ── 地点情報パネル（コントロール下部に表示） ──
        if (infoContent != null) {
            infoContent()
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 波形表示コンポーネント
// ─────────────────────────────────────────────────────────────────────────────

@Composable
private fun WaveformDisplay(
    waveform: FloatArray?,
    progress: Float,
    modifier: Modifier = Modifier
) {
    val primaryColor = MaterialTheme.colorScheme.primary
    val playedColor  = primaryColor
    val unplayedColor = Color.Gray.copy(alpha = 0.5f)

    Canvas(modifier = modifier) {
        if (waveform == null || waveform.isEmpty()) {
            // ローディング中: ダミーバー
            val barCount = 80
            val barWidth = size.width / (barCount * 1.5f)
            val gap      = barWidth * 0.5f
            for (i in 0 until barCount) {
                val x    = i * (barWidth + gap)
                val h    = size.height * (0.1f + 0.2f * kotlin.math.sin(i * 0.5f).toFloat().let { abs(it) })
                val top  = (size.height - h) / 2
                drawRect(
                    color     = unplayedColor,
                    topLeft   = Offset(x, top),
                    size      = androidx.compose.ui.geometry.Size(barWidth, h)
                )
            }
            return@Canvas
        }

        val barCount  = waveform.size
        val barWidth  = size.width / (barCount * 1.3f)
        val gap       = size.width / barCount - barWidth
        val playedIdx = (progress * barCount).toInt().coerceIn(0, barCount)

        waveform.forEachIndexed { i, amp ->
            val x    = i * (barWidth + gap)
            val h    = (amp * size.height * 0.9f).coerceAtLeast(2f)
            val top  = (size.height - h) / 2
            drawRect(
                color   = if (i < playedIdx) playedColor else unplayedColor,
                topLeft = Offset(x, top),
                size    = androidx.compose.ui.geometry.Size(barWidth, h)
            )
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 音声ファイルから PCM 振幅データを生成する（バックグラウンド処理）
// ─────────────────────────────────────────────────────────────────────────────

private suspend fun decodeWaveform(context: Context, filePath: String): FloatArray {
    return kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
        try {
            val extractor = MediaExtractor()
            extractor.setDataSource(filePath)

            // 音声トラックを探す
            var audioTrackIdx = -1
            var format: MediaFormat? = null
            for (i in 0 until extractor.trackCount) {
                val fmt = extractor.getTrackFormat(i)
                if (fmt.getString(MediaFormat.KEY_MIME)?.startsWith("audio/") == true) {
                    audioTrackIdx = i
                    format = fmt
                    break
                }
            }
            if (audioTrackIdx < 0 || format == null) {
                extractor.release()
                return@withContext FloatArray(0)
            }

            extractor.selectTrack(audioTrackIdx)
            val mime    = format.getString(MediaFormat.KEY_MIME)!!
            val codec   = MediaCodec.createDecoderByType(mime)
            codec.configure(format, null, null, 0)
            codec.start()

            val samples  = mutableListOf<Float>()
            val bufInfo  = MediaCodec.BufferInfo()
            var eos      = false

            while (!eos) {
                val inIdx = codec.dequeueInputBuffer(10_000)
                if (inIdx >= 0) {
                    val buf  = codec.getInputBuffer(inIdx) ?: continue
                    val size = extractor.readSampleData(buf, 0)
                    if (size < 0) {
                        codec.queueInputBuffer(inIdx, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                        eos = true
                    } else {
                        codec.queueInputBuffer(inIdx, 0, size, extractor.sampleTime, 0)
                        extractor.advance()
                    }
                }
                val outIdx = codec.dequeueOutputBuffer(bufInfo, 10_000)
                if (outIdx >= 0) {
                    val outBuf = codec.getOutputBuffer(outIdx)
                    if (outBuf != null && bufInfo.size > 0) {
                        val shorts = ShortArray(bufInfo.size / 2)
                        outBuf.order(java.nio.ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts)
                        // ダウンサンプル: 512 サンプルごとに RMS を取る
                        val step = max(1, shorts.size / 512)
                        var i = 0
                        while (i < shorts.size) {
                            val end = min(i + step, shorts.size)
                            var sumSq = 0.0
                            for (j in i until end) sumSq += (shorts[j].toDouble() / Short.MAX_VALUE).let { it * it }
                            samples.add(kotlin.math.sqrt(sumSq / (end - i)).toFloat())
                            i += step
                        }
                    }
                    codec.releaseOutputBuffer(outIdx, false)
                    if (bufInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) eos = true
                }
            }

            codec.stop(); codec.release(); extractor.release()

            // 最大値で正規化して TARGET_BARS 個に圧縮
            val targetBars = 120
            val maxAmp = samples.maxOrNull()?.takeIf { it > 0f } ?: 1f
            val normalized = samples.map { it / maxAmp }
            if (normalized.size <= targetBars) return@withContext normalized.toFloatArray()
            val step = normalized.size.toFloat() / targetBars
            FloatArray(targetBars) { i ->
                val from = (i * step).toInt()
                val to   = ((i + 1) * step).toInt().coerceAtMost(normalized.size)
                normalized.subList(from, to).average().toFloat()
            }
        } catch (e: Exception) {
            FloatArray(0)
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// ユーティリティ
// ─────────────────────────────────────────────────────────────────────────────

private fun formatTime(ms: Long): String {
    val totalSec = ms / 1000
    val min = totalSec / 60
    val sec = totalSec % 60
    return "%d:%02d".format(min, sec)
}
