package com.fieldlabel.collector.ui.screens

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import java.time.LocalDate
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.text.input.KeyboardType
import android.media.MediaMetadataRetriever
import androidx.compose.foundation.Image
import androidx.compose.ui.graphics.asImageBitmap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.delay as coroutineDelay
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
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.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.FileProvider
import androidx.hilt.navigation.compose.hiltViewModel
import coil.compose.AsyncImage
import com.fieldlabel.collector.domain.model.CustomFieldDef
import com.fieldlabel.collector.domain.model.CustomFieldValue
import com.fieldlabel.collector.domain.model.LocationSession
import com.fieldlabel.collector.domain.model.SpecimenPhoto
import com.fieldlabel.collector.service.ConvState
import com.fieldlabel.collector.ui.components.MapPickerView
import com.fieldlabel.collector.ui.viewmodel.BatchPrintState
import com.fieldlabel.collector.ui.viewmodel.CollectionViewModel
import com.fieldlabel.collector.ui.viewmodel.PrintState
import com.fieldlabel.collector.ui.viewmodel.RouteLogViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.google.accompanist.permissions.rememberPermissionState
import java.io.File

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CollectionFormScreen(
    onNavigateToPreview:      (String) -> Unit,
    onNavigateToMediaPreview: (filePath: String, mediaType: String, locationId: String, specimenId: String?) -> Unit = { _, _, _, _ -> },
    viewModel: CollectionViewModel = hiltViewModel(),
    routeLogViewModel: RouteLogViewModel = hiltViewModel()
) {
    val uiState              by viewModel.uiState.collectAsState()
    val collectorName        by viewModel.collectorName.collectAsState(initial = "")
    val sessionPhotos        by viewModel.sessionPhotos.collectAsState()
    val allRecords           by viewModel.allRecords.collectAsState(initial = emptyList())
    val firstPhotoByLocation by viewModel.firstPhotoByLocation.collectAsState()
    val sessionSpecimens     by viewModel.sessionSpecimens.collectAsState()
    val context              = LocalContext.current
    // ハンズフリー
    val hfEnabled       by viewModel.hfEnabled      .collectAsState()
    val hfActive        by viewModel.hfActive        .collectAsState()
    val hfConvState     by viewModel.hfConvState     .collectAsState()
    val hfLastText      by viewModel.hfLastText      .collectAsState()
    val pendingVoiceNotes by viewModel.pendingVoiceNotes.collectAsState()
    // カスタムフィールド
    val allCustomFieldDefs       by viewModel.allCustomFieldDefs.collectAsState()
    val sessionCustomFieldValues by viewModel.sessionCustomFieldValues.collectAsState()
    // 音声録音状態
    val isRecordingAudio by viewModel.isRecordingAudio.collectAsState()
    // 印刷パネル用一括印刷状態
    val sessionBatchPrintState by viewModel.sessionBatchPrintState.collectAsState()

    // 直近地点グループ（上位10件）
    val recentGroups = remember(allRecords) {
        allRecords
            .groupBy { it.locationId }
            .entries
            .sortedByDescending { (_, recs) -> recs.maxOf { it.createdAt } }
            .take(10)
            .map { (id, recs) -> Triple(id, recs.first().address, recs.first().collectionDate) }
    }

    // パーミッション (位置情報)
    val locationPermissions = rememberMultiplePermissionsState(
        listOf(
            android.Manifest.permission.ACCESS_FINE_LOCATION,
            android.Manifest.permission.ACCESS_COARSE_LOCATION
        )
    )
    // パーミッション (Bluetooth — Android 12+)
    val btPermissions = rememberMultiplePermissionsState(
        listOf(android.Manifest.permission.BLUETOOTH_CONNECT)
    )
    // パーミッション (カメラ)
    val cameraPermission = rememberPermissionState(android.Manifest.permission.CAMERA)
    // パーミッション (音声認識)
    val recordAudioPermission = rememberPermissionState(android.Manifest.permission.RECORD_AUDIO)

    var notes by remember { mutableStateOf("") }

    // ハンズフリーの備考を notes フィールドに反映する
    LaunchedEffect(pendingVoiceNotes) {
        if (pendingVoiceNotes.isNotEmpty()) notes = pendingVoiceNotes
    }

    // ── 写真撮影フロー ────────────────────────────────────────────────────
    var pendingPhotoFile   by remember { mutableStateOf<File?>(null) }
    var showPhotoTagDialog by remember { mutableStateOf(false) }
    // ハンズフリーの自動タグモード: null=通常ダイアログ, "location"=地点自動, "specimen"=最新標本自動
    var pendingAutoTagMode by remember { mutableStateOf<String?>(null) }

    // ── 動画撮影フロー ────────────────────────────────────────────────────
    var pendingVideoFile       by remember { mutableStateOf<File?>(null) }
    var showVideoTagDialog     by remember { mutableStateOf(false) }
    var pendingVideoSpecimenId by remember { mutableStateOf<String?>(null) }  // ダイアログ確定後保存用

    // ── 音声録音フロー ────────────────────────────────────────────────────
    var pendingAudioFilePath   by remember { mutableStateOf<String?>(null) }
    var showAudioTagDialog     by remember { mutableStateOf(false) }

    val captureVideoLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.CaptureVideo()
    ) { success ->
        val file = pendingVideoFile
        if (success && file != null) {
            // 動画も写真と同様にタグ付けダイアログを出す
            showVideoTagDialog = true
        } else {
            pendingVideoFile?.delete()
            pendingVideoFile = null
        }
    }

    fun launchVideoCapture() {
        val file = viewModel.createVideoFile()
        pendingVideoFile = file
        val uri = FileProvider.getUriForFile(
            context, "${context.packageName}.fileprovider", file
        )
        captureVideoLauncher.launch(uri)
    }

    val takePictureLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.TakePicture()
    ) { success ->
        if (success) {
            pendingPhotoFile?.let { file ->
                val mode = pendingAutoTagMode
                pendingAutoTagMode = null
                when (mode) {
                    "location" -> viewModel.savePhotoForLocation(file.absolutePath)
                    "specimen" -> viewModel.savePhotoForLatestSpecimen(file.absolutePath)
                    else       -> {
                        viewModel.onPhotoCaptured(file.absolutePath)
                        showPhotoTagDialog = true
                    }
                }
            }
        } else {
            // 撮影をキャンセルした場合（カメラアプリで戻るを押したとき）
            pendingPhotoFile?.delete()
            pendingPhotoFile = null
            pendingAutoTagMode = null
            viewModel.onCameraCancel()
        }
    }

    fun launchCamera() {
        val file = viewModel.createPhotoFile()
        pendingPhotoFile = file
        val uri = FileProvider.getUriForFile(
            context, "${context.packageName}.fileprovider", file
        )
        takePictureLauncher.launch(uri)
    }

    // ハンズフリーのカメラ起動コマンドを受信する（takePictureLauncher の後で宣言）
    LaunchedEffect(Unit) {
        viewModel.voiceOpenCamera.collect {
            pendingAutoTagMode = null   // 通常ダイアログモード
            if (cameraPermission.status.isGranted) launchCamera()
            else cameraPermission.launchPermissionRequest()
        }
    }
    // 「地点写真」コマンド: カメラ起動 → 地点へ自動紐づけ
    LaunchedEffect(Unit) {
        viewModel.voiceOpenCameraForLocation.collect {
            pendingAutoTagMode = "location"
            if (cameraPermission.status.isGranted) launchCamera()
            else cameraPermission.launchPermissionRequest()
        }
    }
    // 「標本写真」コマンド: カメラ起動 → 最新標本へ自動紐づけ
    LaunchedEffect(Unit) {
        viewModel.voiceOpenCameraForSpecimen.collect {
            pendingAutoTagMode = "specimen"
            if (cameraPermission.status.isGranted) launchCamera()
            else cameraPermission.launchPermissionRequest()
        }
    }
    // 「録画」コマンド: 動画録画を起動
    LaunchedEffect(Unit) {
        viewModel.voiceStartVideoCapture.collect {
            if (cameraPermission.status.isGranted) launchVideoCapture()
            else cameraPermission.launchPermissionRequest()
        }
    }
    // 「ログスタート」コマンド: 経路ログ記録を開始
    LaunchedEffect(Unit) {
        viewModel.voiceStartRouteLog.collect {
            routeLogViewModel.startRecording()
        }
    }
    // 「ログストップ」コマンド: 経路ログ記録を停止
    LaunchedEffect(Unit) {
        viewModel.voiceStopRouteLog.collect {
            routeLogViewModel.stopRecording()
        }
    }

    // ── タイマー ──────────────────────────────────────────────────────────────
    val timerEndTimeMs    by viewModel.timerEndTimeMs.collectAsState()
    val timerAlarmActive  by viewModel.timerAlarmActive.collectAsState()
    // 残り時間を毎秒更新するための tick State
    var timerTick by remember { mutableStateOf(0L) }
    LaunchedEffect(timerEndTimeMs) {
        if (timerEndTimeMs != null) {
            while (true) {
                timerTick = System.currentTimeMillis()
                coroutineDelay(1_000L)
            }
        }
    }
    // 残り秒数（表示用）
    val timerRemainingMs = timerEndTimeMs?.let { end ->
        timerTick   // tick で再計算をトリガー
        (end - System.currentTimeMillis()).coerceAtLeast(0L)
    }
    // タイマーカードの展開状態。動作中・アラーム中は自動で開く
    var timerExpanded by remember { mutableStateOf(false) }
    LaunchedEffect(timerRemainingMs, timerAlarmActive) {
        if (timerRemainingMs != null || timerAlarmActive) timerExpanded = true
    }

    val printModeImmediate by viewModel.printModeImmediate.collectAsState(initial = true)

    // ── 印刷/登録ダイアログ ────────────────────────────────────────────────
    when (val ps = uiState.printState) {
        is PrintState.Done -> {
            AlertDialog(
                onDismissRequest = { viewModel.clearPrintState() },
                title = { Text("印刷完了") },
                text  = { Text(ps.message) },
                confirmButton = {
                    TextButton(onClick = {
                        viewModel.clearPrintState()
                        uiState.lastSavedRecord?.let { onNavigateToPreview(it.id) }
                    }) { Text("プレビューを見る") }
                },
                dismissButton = {
                    TextButton(onClick = { viewModel.clearPrintState(); notes = "" }) {
                        Text("次の標本へ")
                    }
                }
            )
        }
        is PrintState.Saved -> {
            AlertDialog(
                onDismissRequest = { viewModel.clearPrintState(); notes = "" },
                title = { Text("登録完了") },
                text  = { Text("${ps.message}\n\n印刷は「地点情報」画面から行えます。") },
                confirmButton = {
                    TextButton(onClick = { viewModel.clearPrintState(); notes = "" }) {
                        Text("次の標本へ")
                    }
                }
            )
        }
        is PrintState.Error -> {
            AlertDialog(
                onDismissRequest = { viewModel.clearPrintState() },
                title = { Text("エラー") },
                text  = { Text(ps.message) },
                confirmButton = {
                    TextButton(onClick = { viewModel.clearPrintState() }) { Text("閉じる") }
                }
            )
        }
        else -> {}
    }

    // ── 印刷パネル用ダイアログ（sessionBatchPrintState） ─────────────────
    when (val sbp = sessionBatchPrintState) {
        is BatchPrintState.Done -> AlertDialog(
            onDismissRequest = { viewModel.clearSessionBatchPrintState() },
            icon  = { Icon(Icons.Default.Print, null) },
            title = { Text("印刷完了") },
            text  = { Text(sbp.message) },
            confirmButton = {
                TextButton(onClick = { viewModel.clearSessionBatchPrintState() }) { Text("閉じる") }
            }
        )
        is BatchPrintState.Error -> AlertDialog(
            onDismissRequest = { viewModel.clearSessionBatchPrintState() },
            title = { Text("印刷エラー") },
            text  = { Text(sbp.message) },
            confirmButton = {
                TextButton(onClick = { viewModel.clearSessionBatchPrintState() }) { Text("閉じる") }
            }
        )
        else -> {}
    }

    // ── 写真タグ付けダイアログ ────────────────────────────────────────────
    val session = uiState.currentSession
    if (showPhotoTagDialog && pendingPhotoFile != null && session != null) {
        PhotoTagDialog(
            photoFile = pendingPhotoFile!!,
            session   = session,
            onConfirm = { specimenId ->
                viewModel.savePhotoWithTag(pendingPhotoFile!!.absolutePath, specimenId)
                pendingPhotoFile   = null
                showPhotoTagDialog = false
            },
            onDismiss = {
                viewModel.cancelPhoto()
                pendingPhotoFile   = null
                showPhotoTagDialog = false
            }
        )
    }

    // ── 動画タグ付けダイアログ ────────────────────────────────────────────
    if (showVideoTagDialog && pendingVideoFile != null && session != null) {
        MediaTagDialog(
            label     = "動画",
            session   = session,
            onConfirm = { specimenId ->
                viewModel.saveVideo(pendingVideoFile!!.absolutePath, specimenId)
                pendingVideoFile   = null
                showVideoTagDialog = false
            },
            onDismiss = {
                pendingVideoFile?.delete()
                pendingVideoFile   = null
                showVideoTagDialog = false
            }
        )
    }

    // ── 音声タグ付けダイアログ ────────────────────────────────────────────
    if (showAudioTagDialog && pendingAudioFilePath != null && session != null) {
        MediaTagDialog(
            label     = "音声",
            session   = session,
            onConfirm = { specimenId ->
                viewModel.saveAudioRecording(pendingAudioFilePath!!, specimenId)
                pendingAudioFilePath = null
                showAudioTagDialog   = false
            },
            onDismiss = {
                File(pendingAudioFilePath!!).delete()
                pendingAudioFilePath = null
                showAudioTagDialog   = false
            }
        )
    }

    val hfScreenLock by viewModel.hfScreenLock.collectAsState()

    Box(modifier = Modifier.fillMaxSize()) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .navigationBarsPadding()
            .imePadding()                    // キーボード表示時に自動スクロール余白を確保
            .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 96.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        // ── ヘッダー ──────────────────────────────────────────────────────
        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Text(
                "採集データ入力",
                style      = MaterialTheme.typography.headlineSmall,
                fontWeight = FontWeight.Bold
            )
            // ハンズフリーボタン（設定で有効時のみ）
            if (hfEnabled) {
                if (!hfActive) {
                    FilledTonalButton(
                        onClick = {
                            if (recordAudioPermission.status.isGranted) {
                                viewModel.startHandsFree()
                            } else {
                                recordAudioPermission.launchPermissionRequest()
                            }
                        },
                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp)
                    ) {
                        Icon(Icons.Default.Mic, null, modifier = Modifier.size(16.dp))
                        Spacer(Modifier.width(4.dp))
                        Text("HF開始", style = MaterialTheme.typography.labelMedium)
                    }
                } else {
                    FilledTonalButton(
                        onClick = { viewModel.stopHandsFree() },
                        colors  = ButtonDefaults.filledTonalButtonColors(
                            containerColor = MaterialTheme.colorScheme.errorContainer,
                            contentColor   = MaterialTheme.colorScheme.onErrorContainer
                        ),
                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp)
                    ) {
                        Icon(Icons.Default.MicOff, null, modifier = Modifier.size(16.dp))
                        Spacer(Modifier.width(4.dp))
                        Text("HF終了", style = MaterialTheme.typography.labelMedium)
                    }
                }
            }
        }

        // ハンズフリーステータスバー
        if (hfActive) {
            val stateLabel = when (hfConvState) {
                ConvState.WAKE_WAIT  -> "ウェイクワード待ち…"
                ConvState.CMD_WAIT   -> "コマンドを話してください…"
                ConvState.PROCESSING -> "処理中…"
                ConvState.SPEAKING   -> "応答中…"
                ConvState.NOTES_WAIT -> "備考を話してください…"
                ConvState.TIMER_WAIT -> "タイマーの分数を話してください…"
                else                 -> "待機中"
            }
            Surface(
                color    = MaterialTheme.colorScheme.primaryContainer,
                shape    = MaterialTheme.shapes.small,
                modifier = Modifier.fillMaxWidth()
            ) {
                Column(modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)) {
                    Row(verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(6.dp)) {
                        Icon(Icons.Default.Mic, null,
                            tint = MaterialTheme.colorScheme.primary,
                            modifier = Modifier.size(14.dp))
                        Text(stateLabel,
                            style = MaterialTheme.typography.labelSmall,
                            color = MaterialTheme.colorScheme.onPrimaryContainer)
                    }
                    if (hfLastText.isNotEmpty()) {
                        Text("「$hfLastText」",
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.7f))
                    }
                }
            }
        }

        // ── タイマーカード ────────────────────────────────────────────────────
        TimerCard(
            timerRemainingMs = timerRemainingMs,
            timerAlarmActive = timerAlarmActive,
            expanded         = timerExpanded,
            onToggleExpand   = { timerExpanded = !timerExpanded },
            onCancel         = { viewModel.cancelTimer() },
            onStopAlarm      = { viewModel.stopAlarm() },
            onStart          = { minutes -> viewModel.startTimerMinutes(minutes) }
        )

        if (session == null) {
            // ── 位置情報取得カード ─────────────────────────────────────
            LocationAcquisitionCard(
                isLoading        = uiState.isLoadingLocation,
                error            = uiState.locationError,
                collectorName    = collectorName,
                recentGroups     = recentGroups,
                onGetLocation    = { name ->
                    if (locationPermissions.allPermissionsGranted) {
                        viewModel.startLocationSession(name)
                    } else {
                        locationPermissions.launchMultiplePermissionRequest()
                    }
                },
                onResumeLocation = { locationId ->
                    viewModel.resumeLocationSession(locationId)
                }
            )

            // ── 直近の地点タイル ───────────────────────────────────────
            if (recentGroups.isNotEmpty()) {
                RecentLocationTilesRow(
                    recentGroups         = recentGroups,
                    firstPhotoByLocation = firstPhotoByLocation,
                    onResume             = { locationId ->
                        viewModel.resumeLocationSession(locationId)
                    }
                )
            }
        } else {
            // ── セッション情報 + 地図 ─────────────────────────────────
            SessionInfoCard(
                session             = session,
                isGeocodingAddress  = uiState.isGeocodingAddress,
                geocodingErrorCode  = uiState.geocodingErrorCode,
                onClear             = { viewModel.clearSession() },
                onLocationAdjusted  = { lat, lon -> viewModel.adjustSessionLocation(lat, lon) }
            )

            // ── 地点レベルのカスタムフィールド ────────────────────────
            val locationFieldDefs = allCustomFieldDefs.filter {
                it.appliesTo == "location" || it.appliesTo == "both"
            }
            if (locationFieldDefs.isNotEmpty()) {
                CustomFieldsSection(
                    title      = "地点の値",
                    fieldDefs  = locationFieldDefs,
                    fieldValues = sessionCustomFieldValues,
                    onSave     = { fieldDefId, value ->
                        viewModel.saveLocationFieldValue(fieldDefId, value)
                    }
                )
            }

            // ── 標本備考入力 ──────────────────────────────────────────
            OutlinedTextField(
                value         = notes,
                onValueChange = { notes = it },
                label         = { Text("備考・メモ") },
                placeholder   = { Text("同定情報、生育環境、特記事項など") },
                leadingIcon   = { Icon(Icons.Default.Notes, null) },
                minLines      = 3,
                maxLines      = 5,
                modifier      = Modifier.fillMaxWidth()
            )

            // ── 標本レベルのカスタムフィールド ────────────────────────
            val specimenFieldDefs = allCustomFieldDefs.filter {
                it.appliesTo == "specimen" || it.appliesTo == "both"
            }
            if (specimenFieldDefs.isNotEmpty()) {
                val nextSpecimenId = "${session.locationId}-%02d".format(session.nextSpecimenNumber)
                CustomFieldsSection(
                    title       = "次の標本の値（標本 ${session.nextSpecimenNumber}）",
                    fieldDefs   = specimenFieldDefs,
                    fieldValues = sessionCustomFieldValues,
                    specimenId  = nextSpecimenId,
                    onSave      = { fieldDefId, value ->
                        viewModel.saveSpecimenFieldValue(fieldDefId, nextSpecimenId, value)
                    }
                )
            }

            // ── Bluetooth 権限警告 ────────────────────────────────────
            if (!btPermissions.allPermissionsGranted) {
                Card(
                    colors   = CardDefaults.cardColors(
                        containerColor = MaterialTheme.colorScheme.errorContainer
                    ),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Column(
                        modifier            = Modifier.padding(12.dp),
                        verticalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        Text(
                            "⚠ Bluetooth接続の権限が必要です（印刷に必須）",
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onErrorContainer
                        )
                        TextButton(
                            onClick = { btPermissions.launchMultiplePermissionRequest() }
                        ) { Text("権限を許可する") }
                    }
                }
            }

            // ── 印刷モードバッジ ──────────────────────────────────────
            if (!printModeImmediate) {
                Surface(
                    color    = MaterialTheme.colorScheme.tertiaryContainer,
                    shape    = MaterialTheme.shapes.small,
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Text(
                        "📦 あとでまとめて印刷モード — 登録のみ行います",
                        style    = MaterialTheme.typography.labelSmall,
                        color    = MaterialTheme.colorScheme.onTertiaryContainer,
                        modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
                    )
                }
            }

            // ── 登録/印刷ボタン ───────────────────────────────────────
            Button(
                onClick  = {
                    if (printModeImmediate && !btPermissions.allPermissionsGranted) {
                        btPermissions.launchMultiplePermissionRequest()
                    } else {
                        viewModel.addSpecimenAndPrint(notes)
                    }
                },
                enabled  = uiState.printState !is PrintState.Printing,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(56.dp)
            ) {
                if (uiState.printState is PrintState.Printing) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(24.dp),
                        color    = MaterialTheme.colorScheme.onPrimary
                    )
                } else {
                    Icon(if (printModeImmediate) Icons.Default.Print else Icons.Default.Save, null)
                    Spacer(Modifier.width(8.dp))
                    Text(
                        if (printModeImmediate)
                            "標本 ${session.nextSpecimenNumber} を保存・印刷"
                        else
                            "標本 ${session.nextSpecimenNumber} を登録する",
                        style = MaterialTheme.typography.titleMedium
                    )
                }
            }

            // ── 採集カウント ──────────────────────────────────────────
            Text(
                text  = "この地点: ${session.nextSpecimenNumber - 1} 点採集済み  " +
                        "地点ID: ${session.locationId}",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.outline
            )

            // ── 採集済み標本一覧（プルダウン） ──────────────────────
            if (sessionSpecimens.isNotEmpty()) {
                SpecimenListSection(
                    specimens = sessionSpecimens,
                    onUpdate  = { viewModel.updateRecord(it) }
                )
            }

            // ── 印刷パネル（標本が1件以上登録されている場合に表示） ──
            if (sessionSpecimens.isNotEmpty()) {
                SessionPrintPanel(
                    locationId         = session.locationId,
                    specimens          = sessionSpecimens,
                    isPrinting         = sessionBatchPrintState is BatchPrintState.Printing,
                    onPrintLocationBatch = { count ->
                        viewModel.printSessionLocationBatch(session.locationId, count)
                    },
                    onPrintAllSpecimens  = {
                        viewModel.printSessionAllSpecimens(session.locationId)
                    },
                    onReprintSpecimen    = { record ->
                        viewModel.reprintSingleSpecimen(record)
                    }
                )
            }

            // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            // メディアセクション（写真・動画・音声）
            // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            HorizontalDivider()

            Row(
                modifier          = Modifier.fillMaxWidth(),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Text(
                    "メディア",
                    style      = MaterialTheme.typography.titleSmall,
                    fontWeight = FontWeight.SemiBold
                )
                if (sessionPhotos.isNotEmpty()) {
                    Text(
                        "${sessionPhotos.count { it.isPhoto }}枚の写真 / " +
                        "${sessionPhotos.count { it.isVideo }}本の動画 / " +
                        "${sessionPhotos.count { it.isAudio }}件の音声",
                        style = MaterialTheme.typography.labelSmall,
                        color = MaterialTheme.colorScheme.outline
                    )
                }
            }

            // メディア取得ボタン行
            Row(
                horizontalArrangement = Arrangement.spacedBy(8.dp),
                modifier = Modifier.fillMaxWidth()
            ) {
                // 写真撮影
                OutlinedButton(
                    onClick  = {
                        if (cameraPermission.status.isGranted) launchCamera()
                        else cameraPermission.launchPermissionRequest()
                    },
                    modifier = Modifier.weight(1f)
                ) {
                    Icon(Icons.Default.CameraAlt, null, modifier = Modifier.size(16.dp))
                    Spacer(Modifier.width(4.dp))
                    Text("写真", style = MaterialTheme.typography.labelMedium)
                }
                // 動画撮影
                OutlinedButton(
                    onClick  = {
                        if (cameraPermission.status.isGranted) launchVideoCapture()
                        else cameraPermission.launchPermissionRequest()
                    },
                    modifier = Modifier.weight(1f)
                ) {
                    Icon(Icons.Default.Videocam, null, modifier = Modifier.size(16.dp))
                    Spacer(Modifier.width(4.dp))
                    Text("動画", style = MaterialTheme.typography.labelMedium)
                }
                // 音声録音
                if (isRecordingAudio) {
                    FilledTonalButton(
                        onClick  = {
                            val path = viewModel.stopAudioRecordingGetPath()
                            if (path != null) {
                                if (hfActive) {
                                    // HFモード: ダイアログを出さずに最新標本へ自動保存し HF を再開
                                    viewModel.saveAudioRecordingForHF(path)
                                } else {
                                    pendingAudioFilePath = path
                                    showAudioTagDialog   = true
                                }
                            }
                        },
                        colors   = ButtonDefaults.filledTonalButtonColors(
                            containerColor = MaterialTheme.colorScheme.errorContainer,
                            contentColor   = MaterialTheme.colorScheme.onErrorContainer
                        ),
                        modifier = Modifier.weight(1f)
                    ) {
                        Icon(Icons.Default.Stop, null, modifier = Modifier.size(16.dp))
                        Spacer(Modifier.width(4.dp))
                        Text("停止", style = MaterialTheme.typography.labelMedium)
                    }
                } else {
                    OutlinedButton(
                        onClick  = {
                            if (recordAudioPermission.status.isGranted) {
                                viewModel.startAudioRecording()
                            } else {
                                recordAudioPermission.launchPermissionRequest()
                            }
                        },
                        modifier = Modifier.weight(1f)
                    ) {
                        Icon(Icons.Default.Mic, null, modifier = Modifier.size(16.dp))
                        Spacer(Modifier.width(4.dp))
                        Text("音声", style = MaterialTheme.typography.labelMedium)
                    }
                }
            }

            // 録音中インジケーター
            if (isRecordingAudio) {
                Surface(
                    color  = MaterialTheme.colorScheme.errorContainer,
                    shape  = MaterialTheme.shapes.small,
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Row(
                        modifier              = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
                        verticalAlignment     = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        Icon(
                            Icons.Default.FiberManualRecord, null,
                            tint     = MaterialTheme.colorScheme.error,
                            modifier = Modifier.size(12.dp)
                        )
                        Text(
                            "録音中…「停止」ボタンで保存",
                            style = MaterialTheme.typography.labelMedium,
                            color = MaterialTheme.colorScheme.onErrorContainer
                        )
                    }
                }
            }

            // 収集済みメディア一覧
            if (sessionPhotos.isNotEmpty()) {
                LazyRow(
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    contentPadding        = PaddingValues(vertical = 4.dp)
                ) {
                    items(sessionPhotos, key = { it.id }) { photo ->
                        MediaThumbnailCard(
                            photo    = photo,
                            session  = session,
                            onTap    = {
                                onNavigateToMediaPreview(photo.filePath, photo.mediaType, photo.locationId, photo.specimenId)
                            },
                            onDelete = { viewModel.deletePhoto(photo) },
                            onRetag  = { specimenId ->
                                viewModel.updatePhotoTag(photo.id, specimenId)
                            }
                        )
                    }
                }
            }
        }
    }   // end Column

    // ── ハンズフリー タッチロックオーバーレイ ─────────────────────────────────
    // ハンズフリーがアクティブかつ画面ロック設定がONのとき、
    // 半透明オーバーレイですべてのタッチを横取りし、長押しでのみ解除できる
    if (hfActive && hfScreenLock) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Black.copy(alpha = 0.25f))
                .pointerInput(Unit) {
                    detectTapGestures(
                        onLongPress = { viewModel.stopHandsFree() },
                        onTap       = { /* タッチを消費してブロック */ }
                    )
                },
            contentAlignment = Alignment.Center
        ) {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                Box(
                    modifier = Modifier
                        .size(64.dp)
                        .background(
                            MaterialTheme.colorScheme.primary.copy(alpha = 0.85f),
                            CircleShape
                        ),
                    contentAlignment = Alignment.Center
                ) {
                    Icon(
                        Icons.Default.Mic,
                        contentDescription = null,
                        tint     = MaterialTheme.colorScheme.onPrimary,
                        modifier = Modifier.size(32.dp)
                    )
                }
                Text(
                    "ハンズフリーモード中",
                    color     = Color.White,
                    fontSize  = 14.sp,
                    fontWeight = FontWeight.SemiBold
                )
                Text(
                    "長押しで解除",
                    color    = Color.White.copy(alpha = 0.75f),
                    fontSize = 12.sp,
                    textAlign = TextAlign.Center
                )
            }
        }
    }

    }   // end Box
}

// ─────────────────────────────────────────────────────────────────────────────
// 直近の地点タイル行
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun RecentLocationTilesRow(
    recentGroups:         List<Triple<String, String?, java.time.LocalDate>>,
    firstPhotoByLocation: Map<String, com.fieldlabel.collector.domain.model.SpecimenPhoto>,
    onResume:             (String) -> Unit
) {
    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        Text(
            "最近の記録",
            style      = MaterialTheme.typography.titleSmall,
            fontWeight = FontWeight.SemiBold
        )
        LazyRow(
            horizontalArrangement = Arrangement.spacedBy(10.dp),
            contentPadding        = PaddingValues(vertical = 2.dp)
        ) {
            items(recentGroups, key = { it.first }) { (locationId, address, date) ->
                val photo = firstPhotoByLocation[locationId]
                RecentLocationTile(
                    locationId = locationId,
                    address    = address,
                    date       = date,
                    photoPath  = photo?.filePath,
                    onClick    = { onResume(locationId) }
                )
            }
        }
    }
}

@Composable
private fun RecentLocationTile(
    locationId: String,
    address:    String?,
    date:       java.time.LocalDate,
    photoPath:  String?,
    onClick:    () -> Unit
) {
    Card(
        modifier  = Modifier
            .width(140.dp)
            .clickable(onClick = onClick),
        shape     = RoundedCornerShape(12.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
    ) {
        Column {
            // ── サムネイル部分 ────────────────────────────────────────
            Box(
                modifier            = Modifier
                    .fillMaxWidth()
                    .height(88.dp)
                    .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)),
                contentAlignment    = Alignment.Center
            ) {
                if (photoPath != null) {
                    AsyncImage(
                        model              = java.io.File(photoPath),
                        contentDescription = "採集写真",
                        contentScale       = ContentScale.Crop,
                        modifier           = Modifier.fillMaxSize()
                    )
                } else {
                    // 写真なし：アイコンプレースホルダー
                    Surface(
                        modifier = Modifier.fillMaxSize(),
                        color    = MaterialTheme.colorScheme.surfaceVariant
                    ) {}
                    Icon(
                        imageVector        = Icons.Default.Place,
                        contentDescription = null,
                        modifier           = Modifier.size(36.dp),
                        tint               = MaterialTheme.colorScheme.primary.copy(alpha = 0.7f)
                    )
                }
            }

            // ── 地点情報部分 ──────────────────────────────────────────
            Column(
                modifier            = Modifier.padding(horizontal = 8.dp, vertical = 6.dp),
                verticalArrangement = Arrangement.spacedBy(2.dp)
            ) {
                Text(
                    text     = locationId,
                    style    = MaterialTheme.typography.labelMedium,
                    color    = MaterialTheme.colorScheme.primary,
                    maxLines = 1,
                    overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
                )
                if (!address.isNullOrBlank()) {
                    Text(
                        text     = address,
                        style    = MaterialTheme.typography.bodySmall,
                        maxLines = 2,
                        overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
                        color    = MaterialTheme.colorScheme.onSurface
                    )
                }
                Text(
                    text  = date.toString(),
                    style = MaterialTheme.typography.labelSmall,
                    color = MaterialTheme.colorScheme.outline
                )
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 位置情報取得カード
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun LocationAcquisitionCard(
    isLoading: Boolean,
    error: String?,
    collectorName: String,
    recentGroups: List<Triple<String, String?, java.time.LocalDate>>,
    onGetLocation: (String) -> Unit,
    onResumeLocation: (String) -> Unit
) {
    var name by remember(collectorName) { mutableStateOf(collectorName) }
    var showResumeDialog by remember { mutableStateOf(false) }

    // 過去地点選択ダイアログ
    if (showResumeDialog) {
        AlertDialog(
            onDismissRequest = { showResumeDialog = false },
            title = { Text("再開する地点を選択") },
            text  = {
                if (recentGroups.isEmpty()) {
                    Text("過去の記録がありません", style = MaterialTheme.typography.bodyMedium)
                } else {
                    LazyColumn(
                        verticalArrangement = Arrangement.spacedBy(4.dp),
                        modifier = Modifier.heightIn(max = 360.dp)
                    ) {
                        items(recentGroups) { (locationId, address, date) ->
                            Surface(
                                shape    = MaterialTheme.shapes.small,
                                tonalElevation = 2.dp,
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .clickable {
                                        showResumeDialog = false
                                        onResumeLocation(locationId)
                                    }
                            ) {
                                Column(modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)) {
                                    Text(locationId, style = MaterialTheme.typography.labelMedium,
                                        color = MaterialTheme.colorScheme.primary)
                                    if (!address.isNullOrBlank()) {
                                        Text(address, style = MaterialTheme.typography.bodySmall,
                                            color = MaterialTheme.colorScheme.onSurface)
                                    }
                                    Text(date.toString(), style = MaterialTheme.typography.bodySmall,
                                        color = MaterialTheme.colorScheme.outline)
                                }
                            }
                        }
                    }
                }
            },
            confirmButton = {
                TextButton(onClick = { showResumeDialog = false }) { Text("キャンセル") }
            }
        )
    }

    Card(modifier = Modifier.fillMaxWidth()) {
        Column(
            modifier = Modifier.padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Text("採集地点の設定", style = MaterialTheme.typography.titleMedium)

            OutlinedTextField(
                value         = name,
                onValueChange = { name = it },
                label         = { Text("採集者名") },
                leadingIcon   = { Icon(Icons.Default.Person, null) },
                singleLine    = true,
                modifier      = Modifier.fillMaxWidth()
            )

            if (error != null) {
                Text(error, color = MaterialTheme.colorScheme.error,
                    style = MaterialTheme.typography.bodySmall)
            }

            Button(
                onClick  = { onGetLocation(name) },
                enabled  = !isLoading && name.isNotBlank(),
                modifier = Modifier.fillMaxWidth()
            ) {
                if (isLoading) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(20.dp),
                        color    = MaterialTheme.colorScheme.onPrimary
                    )
                    Spacer(Modifier.width(8.dp))
                    Text("GPS取得中…")
                } else {
                    Icon(Icons.Default.MyLocation, null)
                    Spacer(Modifier.width(8.dp))
                    Text("現在地を取得して開始")
                }
            }

            OutlinedButton(
                onClick  = { showResumeDialog = true },
                enabled  = !isLoading,
                modifier = Modifier.fillMaxWidth()
            ) {
                Icon(Icons.Default.History, null)
                Spacer(Modifier.width(8.dp))
                Text("過去の記録の続きで開始")
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// セッション情報カード
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun SessionInfoCard(
    session:            LocationSession,
    isGeocodingAddress: Boolean = false,
    geocodingErrorCode: String?  = null,
    onClear:            () -> Unit,
    onLocationAdjusted: (Double, Double) -> Unit
) {
    var showMap by remember { mutableStateOf(false) }

    Card(
        modifier = Modifier.fillMaxWidth(),
        colors   = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.primaryContainer
        )
    ) {
        Column(
            modifier            = Modifier.padding(12.dp),
            verticalArrangement = Arrangement.spacedBy(6.dp)
        ) {
            Row(
                modifier              = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment     = Alignment.CenterVertically
            ) {
                Text(
                    "現在の採集地点",
                    style      = MaterialTheme.typography.titleSmall,
                    fontWeight = FontWeight.Bold
                )
                Button(
                    onClick = onClear,
                    colors  = ButtonDefaults.buttonColors(
                        containerColor = MaterialTheme.colorScheme.surface,
                        contentColor   = MaterialTheme.colorScheme.primary
                    ),
                    contentPadding = PaddingValues(horizontal = 14.dp, vertical = 0.dp)
                ) {
                    Icon(Icons.Default.Edit, null, modifier = Modifier.size(14.dp))
                    Spacer(Modifier.width(4.dp))
                    Text("地点変更", style = MaterialTheme.typography.labelMedium)
                }
            }

            Text("地点ID: ${session.locationId}", style = MaterialTheme.typography.bodyMedium)

            // 住所表示：取得中 / エラー / 成功（部分住所含む）/ 座標フォールバック
            when {
                isGeocodingAddress -> {
                    Row(
                        verticalAlignment     = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(6.dp)
                    ) {
                        CircularProgressIndicator(
                            modifier    = Modifier.size(12.dp),
                            strokeWidth = 1.5.dp,
                            color       = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.6f)
                        )
                        Text(
                            "場所: 住所取得中…",
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.6f)
                        )
                    }
                }
                geocodingErrorCode != null -> {
                    // エラー時: 座標 + エラーコードを表示
                    val coordText = "${"%.5f".format(session.latitude)}°N, ${"%.5f".format(session.longitude)}°E"
                    Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
                        Text(
                            "場所: $coordText",
                            style = MaterialTheme.typography.bodySmall
                        )
                        Text(
                            "⚠ 住所取得失敗 [$geocodingErrorCode]",
                            style = MaterialTheme.typography.labelSmall,
                            color = MaterialTheme.colorScheme.error.copy(alpha = 0.8f)
                        )
                    }
                }
                else -> {
                    // 成功: 住所（部分住所も含めそのまま表示）。取れなければ座標
                    val locText = if (!session.address.isNullOrBlank()) session.address
                                  else "${"%.5f".format(session.latitude)}°N, ${"%.5f".format(session.longitude)}°E"
                    Text("場所: $locText", style = MaterialTheme.typography.bodySmall)
                }
            }
            val altSuffix = session.altitude
                ?.let { "  標高: ${"%.1f".format(it)}m" }
                ?: "  標高: 未取得"
            Text(
                "GPS: ${"%.6f".format(session.latitude)}°N,  ${"%.6f".format(session.longitude)}°E$altSuffix",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.75f)
            )
            // 採集日：セッション開始日と今日が異なれば範囲表示
            val today = LocalDate.now()
            val dateLabel = if (session.collectionDate != today) {
                val fmt = { d: LocalDate ->
                    "${d.year}/${"%02d".format(d.monthValue)}/${"%02d".format(d.dayOfMonth)}"
                }
                "${fmt(session.collectionDate)}〜${fmt(today)}（複数日）"
            } else {
                session.collectionDate.toString()
            }
            Text(
                "採集日: $dateLabel  採集者: ${session.collectorName}",
                style = MaterialTheme.typography.bodySmall
            )

            OutlinedButton(
                onClick  = { showMap = !showMap },
                modifier = Modifier.fillMaxWidth(),
                colors   = ButtonDefaults.outlinedButtonColors(
                    contentColor = MaterialTheme.colorScheme.onPrimaryContainer
                )
            ) {
                Icon(Icons.Default.Map, null, modifier = Modifier.size(18.dp))
                Spacer(Modifier.width(6.dp))
                Text(if (showMap) "地図を閉じる" else "地図で確認・位置調整")
            }

            AnimatedVisibility(
                visible = showMap,
                enter   = expandVertically(),
                exit    = shrinkVertically()
            ) {
                Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
                    Text(
                        "地図を動かして中央の十字に合わせ「この地点に修正」を押してください",
                        style = MaterialTheme.typography.labelSmall,
                        color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.75f)
                    )
                    MapPickerView(
                        latitude          = session.latitude,
                        longitude         = session.longitude,
                        modifier          = Modifier
                            .fillMaxWidth()
                            .height(300.dp),
                        onPositionChanged = { lat, lon -> onLocationAdjusted(lat, lon) },
                        onConfirmed       = { showMap = false }   // 確定後に地図を閉じる
                    )
                }
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// タイマーカード（プルダウン式。動作中・アラーム中は自動展開）
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun TimerCard(
    timerRemainingMs: Long?,
    timerAlarmActive: Boolean,
    expanded:         Boolean,
    onToggleExpand:   () -> Unit,
    onCancel:         () -> Unit,
    onStopAlarm:      () -> Unit,
    onStart:          (Int) -> Unit
) {
    var inputMinutes by remember { mutableStateOf("") }

    // アラーム中は error-container、カウント中は tertiaryContainer、それ以外はデフォルト
    val cardColors = when {
        timerAlarmActive  -> CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer)
        timerRemainingMs != null -> CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.tertiaryContainer)
        else              -> CardDefaults.cardColors()
    }
    val headerIconTint = when {
        timerAlarmActive  -> MaterialTheme.colorScheme.onErrorContainer
        timerRemainingMs != null -> MaterialTheme.colorScheme.tertiary
        else              -> MaterialTheme.colorScheme.onSurfaceVariant
    }
    val headerTextColor = when {
        timerAlarmActive  -> MaterialTheme.colorScheme.onErrorContainer
        timerRemainingMs != null -> MaterialTheme.colorScheme.onTertiaryContainer
        else              -> MaterialTheme.colorScheme.onSurface
    }

    Card(
        modifier = Modifier.fillMaxWidth(),
        colors   = cardColors
    ) {
        Column(modifier = Modifier.fillMaxWidth()) {
            // ── ヘッダー行（タップで展開／折りたたみ） ──────────────────────
            Row(
                modifier              = Modifier
                    .fillMaxWidth()
                    .clickable(onClick = onToggleExpand)
                    .padding(horizontal = 12.dp, vertical = 10.dp),
                verticalAlignment     = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                Icon(
                    if (timerAlarmActive) Icons.Default.Alarm else Icons.Default.Timer,
                    null,
                    tint     = headerIconTint,
                    modifier = Modifier.size(20.dp)
                )
                Text(
                    text = when {
                        timerAlarmActive -> "タイマー終了！"
                        timerRemainingMs != null -> {
                            val min = (timerRemainingMs / 60_000).toInt()
                            val sec = ((timerRemainingMs % 60_000) / 1000).toInt()
                            "タイマー　残り " + if (min > 0) "%d分 %02d秒".format(min, sec) else "%d秒".format(sec)
                        }
                        else -> "タイマー"
                    },
                    style      = MaterialTheme.typography.titleSmall,
                    fontWeight = FontWeight.SemiBold,
                    color      = headerTextColor,
                    modifier   = Modifier.weight(1f)
                )
                Icon(
                    if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
                    contentDescription = if (expanded) "折りたたむ" else "展開する",
                    tint     = headerTextColor.copy(alpha = 0.7f),
                    modifier = Modifier.size(20.dp)
                )
            }

            // ── 展開コンテンツ ────────────────────────────────────────────────
            AnimatedVisibility(
                visible = expanded,
                enter   = expandVertically(),
                exit    = shrinkVertically()
            ) {
                Column(
                    modifier            = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 12.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    HorizontalDivider(color = headerTextColor.copy(alpha = 0.15f))

                    when {
                        timerAlarmActive -> {
                            // ── アラーム停止ボタン ──
                            Button(
                                onClick = onStopAlarm,
                                modifier = Modifier.fillMaxWidth(),
                                colors   = ButtonDefaults.buttonColors(
                                    containerColor = MaterialTheme.colorScheme.error,
                                    contentColor   = MaterialTheme.colorScheme.onError
                                )
                            ) {
                                Icon(Icons.Default.AlarmOff, null, modifier = Modifier.size(18.dp))
                                Spacer(Modifier.width(6.dp))
                                Text("アラームを止める", style = MaterialTheme.typography.labelLarge)
                            }
                        }
                        timerRemainingMs != null -> {
                            // ── 残り時間表示 ──
                            val min = (timerRemainingMs / 60_000).toInt()
                            val sec = ((timerRemainingMs % 60_000) / 1000).toInt()
                            val timeText = if (min > 0) "%d分 %02d秒".format(min, sec) else "%d秒".format(sec)
                            Row(
                                modifier              = Modifier.fillMaxWidth(),
                                horizontalArrangement = Arrangement.SpaceBetween,
                                verticalAlignment     = Alignment.CenterVertically
                            ) {
                                Text(
                                    "残り $timeText",
                                    style      = MaterialTheme.typography.headlineSmall,
                                    fontWeight = FontWeight.Bold,
                                    color      = MaterialTheme.colorScheme.onTertiaryContainer
                                )
                                OutlinedButton(
                                    onClick = onCancel,
                                    colors  = ButtonDefaults.outlinedButtonColors(
                                        contentColor = MaterialTheme.colorScheme.onTertiaryContainer
                                    )
                                ) {
                                    Icon(Icons.Default.Stop, null, modifier = Modifier.size(16.dp))
                                    Spacer(Modifier.width(4.dp))
                                    Text("キャンセル", style = MaterialTheme.typography.labelMedium)
                                }
                            }
                        }
                        else -> {
                            // ── タイマーセット UI ──
                            Row(
                                horizontalArrangement = Arrangement.spacedBy(8.dp),
                                verticalAlignment     = Alignment.CenterVertically
                            ) {
                                OutlinedTextField(
                                    value         = inputMinutes,
                                    onValueChange = { v -> if (v.length <= 3 && v.all { it.isDigit() }) inputMinutes = v },
                                    label         = { Text("分") },
                                    singleLine    = true,
                                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                                    modifier      = Modifier.weight(1f)
                                )
                                Button(
                                    onClick  = {
                                        val m = inputMinutes.toIntOrNull()
                                        if (m != null && m in 1..180) {
                                            onStart(m)
                                            inputMinutes = ""
                                        }
                                    },
                                    enabled  = inputMinutes.toIntOrNull()?.let { it in 1..180 } == true
                                ) {
                                    Icon(Icons.Default.PlayArrow, null, modifier = Modifier.size(18.dp))
                                    Spacer(Modifier.width(4.dp))
                                    Text("セット")
                                }
                            }
                            Text(
                                "1〜180分で設定。HFモードでは「タイマー」コマンドでも設定できます。",
                                style = MaterialTheme.typography.labelSmall,
                                color = MaterialTheme.colorScheme.outline
                            )
                        }
                    }
                }
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// メディアサムネイルカード（写真・動画・音声共用）
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun MediaThumbnailCard(
    photo:    SpecimenPhoto,
    session:  LocationSession,
    onTap:    () -> Unit,
    onDelete: () -> Unit,
    onRetag:  (specimenId: String?) -> Unit
) {
    var showRetagDialog   by remember { mutableStateOf(false) }
    var showDeleteConfirm by remember { mutableStateOf(false) }

    if (showRetagDialog) {
        MediaTagDialog(
            label      = when {
                photo.isVideo -> "動画"
                photo.isAudio -> "音声"
                else          -> "写真"
            },
            session    = session,
            initialTag = photo.specimenId,
            onConfirm  = { specimenId -> onRetag(specimenId); showRetagDialog = false },
            onDismiss  = { showRetagDialog = false }
        )
    }
    if (showDeleteConfirm) {
        val label = when {
            photo.isVideo -> "動画"
            photo.isAudio -> "音声"
            else          -> "写真"
        }
        AlertDialog(
            onDismissRequest = { showDeleteConfirm = false },
            title = { Text("${label}を削除") },
            text  = { Text("この${label}を削除しますか？") },
            confirmButton = {
                TextButton(
                    onClick = { onDelete(); showDeleteConfirm = false },
                    colors  = ButtonDefaults.textButtonColors(
                        contentColor = MaterialTheme.colorScheme.error
                    )
                ) { Text("削除") }
            },
            dismissButton = {
                TextButton(onClick = { showDeleteConfirm = false }) { Text("キャンセル") }
            }
        )
    }

    // 動画サムネイル: IO スレッドで最初のフレームを抽出してキャッシュする
    var videoThumbnail by remember(photo.filePath) {
        mutableStateOf<android.graphics.Bitmap?>(null)
    }
    LaunchedEffect(photo.filePath) {
        if (!photo.isVideo) return@LaunchedEffect
        videoThumbnail = withContext(Dispatchers.IO) {
            try {
                val retriever = MediaMetadataRetriever()
                retriever.setDataSource(photo.filePath)
                val bmp = retriever.getFrameAtTime(
                    0L, MediaMetadataRetriever.OPTION_CLOSEST_SYNC
                )
                retriever.release()
                bmp
            } catch (_: Exception) { null }
        }
    }

    Card(modifier = Modifier.width(130.dp)) {
        Column {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(90.dp)
                    .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
                    .background(MaterialTheme.colorScheme.surfaceVariant)
                    .clickable(onClick = onTap),
                contentAlignment = Alignment.Center
            ) {
                when {
                    photo.isPhoto -> {
                        AsyncImage(
                            model              = File(photo.filePath),
                            contentDescription = "採集写真",
                            contentScale       = ContentScale.Crop,
                            modifier           = Modifier.fillMaxSize()
                        )
                    }
                    photo.isVideo -> {
                        val bmp = videoThumbnail
                        if (bmp != null) {
                            Image(
                                bitmap             = bmp.asImageBitmap(),
                                contentDescription = "動画サムネイル",
                                contentScale       = ContentScale.Crop,
                                modifier           = Modifier.fillMaxSize()
                            )
                        } else {
                            // サムネイル読込中はアイコンを表示
                            Icon(
                                imageVector        = Icons.Default.Videocam,
                                contentDescription = null,
                                modifier           = Modifier.size(36.dp),
                                tint               = MaterialTheme.colorScheme.onSurfaceVariant
                            )
                        }
                        // 再生アイコンオーバーレイ
                        Icon(
                            imageVector        = Icons.Default.PlayCircle,
                            contentDescription = null,
                            tint               = Color.White.copy(alpha = 0.85f),
                            modifier           = Modifier
                                .size(32.dp)
                                .align(Alignment.BottomEnd)
                                .padding(4.dp)
                        )
                    }
                    else -> {
                        // 音声
                        Icon(
                            imageVector        = Icons.Default.Mic,
                            contentDescription = null,
                            modifier           = Modifier.size(36.dp),
                            tint               = MaterialTheme.colorScheme.onSurfaceVariant
                        )
                    }
                }
            }
            Row(
                modifier          = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 6.dp, vertical = 4.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                val tagText = if (photo.specimenId != null)
                    "標本" + photo.specimenId.takeLast(3)
                else "地点"
                Text(
                    text     = tagText,
                    style    = MaterialTheme.typography.labelSmall,
                    color    = MaterialTheme.colorScheme.primary,
                    modifier = Modifier.weight(1f)
                )
                IconButton(
                    onClick  = { showRetagDialog = true },
                    modifier = Modifier.size(24.dp)
                ) {
                    Icon(
                        Icons.Default.Edit, "タグ変更",
                        modifier = Modifier.size(14.dp),
                        tint     = MaterialTheme.colorScheme.onSurfaceVariant
                    )
                }
                IconButton(
                    onClick  = { showDeleteConfirm = true },
                    modifier = Modifier.size(24.dp)
                ) {
                    Icon(
                        Icons.Default.Delete, "削除",
                        modifier = Modifier.size(14.dp),
                        tint     = MaterialTheme.colorScheme.error
                    )
                }
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// メディアタグ付けダイアログ（写真以外のメディア用）
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun MediaTagDialog(
    label:      String,
    session:    LocationSession,
    initialTag: String? = null,
    onConfirm:  (specimenId: String?) -> Unit,
    onDismiss:  () -> Unit
) {
    var selectedTag by remember(initialTag) { mutableStateOf(initialTag) }

    AlertDialog(
        onDismissRequest = onDismiss,
        title  = { Text("${label}を紐づける") },
        text   = {
            Column(modifier = Modifier.fillMaxWidth()) {
                Text(
                    "紐づけるラベルを選択",
                    style    = MaterialTheme.typography.labelMedium,
                    modifier = Modifier.padding(bottom = 8.dp)
                )
                // ── スクロール可能な選択リスト ────────────────────────────
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .heightIn(max = 320.dp)
                        .verticalScroll(rememberScrollState()),
                    verticalArrangement = Arrangement.spacedBy(0.dp)
                ) {
                    // 地点全体オプション
                    Row(
                        modifier          = Modifier
                            .fillMaxWidth()
                            .clickable { selectedTag = null },
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        RadioButton(selected = selectedTag == null, onClick = { selectedTag = null })
                        Spacer(Modifier.width(4.dp))
                        Column {
                            Text("地点全体", style = MaterialTheme.typography.bodyMedium)
                            Text(
                                session.locationId,
                                style = MaterialTheme.typography.labelSmall,
                                color = MaterialTheme.colorScheme.outline
                            )
                        }
                    }

                    // 採集済み標本
                    if (session.nextSpecimenNumber > 1) {
                        HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
                        for (i in 1 until session.nextSpecimenNumber) {
                            val specId = "${session.locationId}-%02d".format(i)
                            Row(
                                modifier          = Modifier
                                    .fillMaxWidth()
                                    .clickable { selectedTag = specId },
                                verticalAlignment = Alignment.CenterVertically
                            ) {
                                RadioButton(
                                    selected = selectedTag == specId,
                                    onClick  = { selectedTag = specId }
                                )
                                Spacer(Modifier.width(4.dp))
                                Column {
                                    Text("標本 $i", style = MaterialTheme.typography.bodyMedium)
                                    Text(
                                        specId,
                                        style = MaterialTheme.typography.labelSmall,
                                        color = MaterialTheme.colorScheme.outline
                                    )
                                }
                            }
                        }
                    }
                }
            }
        },
        confirmButton = {
            TextButton(onClick = { onConfirm(selectedTag) }) { Text("保存") }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) { Text("キャンセル") }
        }
    )
}

// ─────────────────────────────────────────────────────────────────────────────
// カスタムフィールドセクション
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun CustomFieldsSection(
    title:       String,
    fieldDefs:   List<CustomFieldDef>,
    fieldValues: List<CustomFieldValue>,
    specimenId:  String? = null,
    onSave:      (fieldDefId: String, value: String) -> Unit
) {
    // 各フィールドの入力値を管理
    val textStates = remember(fieldDefs, specimenId) {
        fieldDefs.associate { def ->
            val existing = fieldValues.firstOrNull { fv ->
                fv.fieldDefId == def.id &&
                (specimenId == null || fv.specimenId == specimenId)
            }?.value ?: ""
            def.id to mutableStateOf(existing)
        }
    }

    Card(
        modifier = Modifier.fillMaxWidth(),
        colors   = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.4f)
        )
    ) {
        Column(
            modifier            = Modifier.padding(12.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp)
        ) {
            Text(
                title,
                style      = MaterialTheme.typography.titleSmall,
                fontWeight = FontWeight.SemiBold,
                color      = MaterialTheme.colorScheme.onSecondaryContainer
            )
            fieldDefs.forEach { def ->
                val state = textStates[def.id] ?: return@forEach
                OutlinedTextField(
                    value         = state.value,
                    onValueChange = { state.value = it },
                    label         = { Text(def.displayLabel) },
                    singleLine    = true,
                    keyboardOptions = if (def.valueType == "number")
                        KeyboardOptions(keyboardType = KeyboardType.Decimal)
                    else KeyboardOptions.Default,
                    trailingIcon  = {
                        if (state.value.isNotBlank()) {
                            IconButton(onClick = { onSave(def.id, state.value) }) {
                                Icon(
                                    Icons.Default.Check, "保存",
                                    tint = MaterialTheme.colorScheme.primary
                                )
                            }
                        }
                    },
                    modifier      = Modifier.fillMaxWidth()
                )
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 印刷パネル（採集画面内）
// ─────────────────────────────────────────────────────────────────────────────

@Composable
private fun SessionPrintPanel(
    locationId:           String,
    specimens:            List<com.fieldlabel.collector.domain.model.CollectionRecord>,
    isPrinting:           Boolean,
    onPrintLocationBatch: (count: Int) -> Unit,
    onPrintAllSpecimens:  () -> Unit,
    onReprintSpecimen:    (com.fieldlabel.collector.domain.model.CollectionRecord) -> Unit
) {
    var expanded          by remember { mutableStateOf(false) }
    var locationCount     by remember { mutableStateOf(1) }

    Card(
        modifier = Modifier.fillMaxWidth(),
        colors   = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.45f)
        )
    ) {
        Column {
            // ── ヘッダー（タップで開閉） ──────────────────────────────
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable { expanded = !expanded }
                    .padding(horizontal = 12.dp, vertical = 10.dp),
                verticalAlignment     = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Row(
                    verticalAlignment     = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    Icon(
                        Icons.Default.Print, null,
                        modifier = Modifier.size(18.dp),
                        tint     = MaterialTheme.colorScheme.secondary
                    )
                    Text(
                        "印刷パネル",
                        style      = MaterialTheme.typography.titleSmall,
                        fontWeight = FontWeight.SemiBold
                    )
                }
                Icon(
                    if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
                    null,
                    modifier = Modifier.size(18.dp),
                    tint     = MaterialTheme.colorScheme.outline
                )
            }

            if (expanded) {
                HorizontalDivider(modifier = Modifier.padding(horizontal = 12.dp))

                Column(
                    modifier            = Modifier.padding(horizontal = 12.dp, vertical = 10.dp),
                    verticalArrangement = Arrangement.spacedBy(14.dp)
                ) {

                    // ── 地点ラベル枚数指定印刷 ──────────────────────────
                    Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
                        Text(
                            "地点ラベル",
                            style      = MaterialTheme.typography.labelMedium,
                            fontWeight = FontWeight.SemiBold,
                            color      = MaterialTheme.colorScheme.onSurfaceVariant
                        )
                        Row(
                            verticalAlignment     = Alignment.CenterVertically,
                            horizontalArrangement = Arrangement.spacedBy(6.dp)
                        ) {
                            // 枚数ステッパー（−ボタン）
                            OutlinedButton(
                                onClick          = { if (locationCount > 1) locationCount-- },
                                enabled          = locationCount > 1 && !isPrinting,
                                modifier         = Modifier.size(36.dp),
                                contentPadding   = PaddingValues(0.dp),
                                shape            = MaterialTheme.shapes.small
                            ) {
                                Icon(Icons.Default.Remove, "1枚減らす", modifier = Modifier.size(14.dp))
                            }
                            Text(
                                "${locationCount}枚",
                                style      = MaterialTheme.typography.titleMedium,
                                fontWeight = FontWeight.Bold,
                                modifier   = Modifier.widthIn(min = 40.dp),
                                textAlign  = TextAlign.Center
                            )
                            // 枚数ステッパー（＋ボタン）
                            OutlinedButton(
                                onClick          = { locationCount++ },
                                enabled          = locationCount < 99 && !isPrinting,
                                modifier         = Modifier.size(36.dp),
                                contentPadding   = PaddingValues(0.dp),
                                shape            = MaterialTheme.shapes.small
                            ) {
                                Icon(Icons.Default.Add, "1枚増やす", modifier = Modifier.size(14.dp))
                            }
                            Spacer(Modifier.width(2.dp))
                            // 印刷ボタン
                            FilledTonalButton(
                                onClick  = { onPrintLocationBatch(locationCount) },
                                enabled  = !isPrinting,
                                modifier = Modifier.weight(1f)
                            ) {
                                if (isPrinting) {
                                    CircularProgressIndicator(
                                        modifier    = Modifier.size(16.dp),
                                        strokeWidth = 2.dp
                                    )
                                } else {
                                    Icon(Icons.Default.Print, null, modifier = Modifier.size(16.dp))
                                    Spacer(Modifier.width(4.dp))
                                    Text(
                                        "地点ラベルを印刷",
                                        style = MaterialTheme.typography.labelMedium
                                    )
                                }
                            }
                        }
                    }

                    HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)

                    // ── 標本ラベル一括印刷 ────────────────────────────────
                    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                        Text(
                            "標本ラベル（${specimens.size}点）",
                            style      = MaterialTheme.typography.labelMedium,
                            fontWeight = FontWeight.SemiBold,
                            color      = MaterialTheme.colorScheme.onSurfaceVariant
                        )
                        // 全標本一括印刷ボタン
                        FilledTonalButton(
                            onClick  = onPrintAllSpecimens,
                            enabled  = !isPrinting,
                            modifier = Modifier.fillMaxWidth()
                        ) {
                            if (isPrinting) {
                                CircularProgressIndicator(
                                    modifier = Modifier.size(16.dp),
                                    strokeWidth = 2.dp
                                )
                                Spacer(Modifier.width(6.dp))
                                Text("印刷中…", style = MaterialTheme.typography.labelMedium)
                            } else {
                                Icon(Icons.Default.Print, null, modifier = Modifier.size(16.dp))
                                Spacer(Modifier.width(6.dp))
                                Text(
                                    "全 ${specimens.size} 標本を一括印刷",
                                    style = MaterialTheme.typography.labelMedium
                                )
                            }
                        }

                        // 各標本の個別再印刷リスト
                        specimens.forEach { specimen ->
                            Surface(
                                shape         = MaterialTheme.shapes.small,
                                tonalElevation = 2.dp,
                                modifier      = Modifier.fillMaxWidth()
                            ) {
                                Row(
                                    modifier          = Modifier
                                        .fillMaxWidth()
                                        .padding(start = 12.dp, end = 4.dp, top = 4.dp, bottom = 4.dp),
                                    verticalAlignment = Alignment.CenterVertically
                                ) {
                                    Column(modifier = Modifier.weight(1f)) {
                                        Text(
                                            "標本 ${specimen.specimenNumber}",
                                            style      = MaterialTheme.typography.labelMedium,
                                            fontWeight = FontWeight.SemiBold,
                                            color      = MaterialTheme.colorScheme.primary
                                        )
                                        Text(
                                            specimen.specimenId,
                                            style = MaterialTheme.typography.labelSmall,
                                            color = MaterialTheme.colorScheme.outline,
                                            fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace
                                        )
                                    }
                                    IconButton(
                                        onClick  = { onReprintSpecimen(specimen) },
                                        enabled  = !isPrinting,
                                        modifier = Modifier.size(40.dp)
                                    ) {
                                        Icon(
                                            Icons.Default.Print,
                                            contentDescription = "${specimen.specimenId} を再印刷",
                                            modifier = Modifier.size(18.dp),
                                            tint = if (!isPrinting)
                                                MaterialTheme.colorScheme.secondary
                                            else
                                                MaterialTheme.colorScheme.outline
                                        )
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 採集済み標本一覧セクション（プルダウン）
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun SpecimenListSection(
    specimens: List<com.fieldlabel.collector.domain.model.CollectionRecord>,
    onUpdate:  (com.fieldlabel.collector.domain.model.CollectionRecord) -> Unit
) {
    var expanded by remember { mutableStateOf(false) }
    var editingRecord by remember { mutableStateOf<com.fieldlabel.collector.domain.model.CollectionRecord?>(null) }

    // 編集ダイアログ
    editingRecord?.let { record ->
        SpecimenEditDialog(
            record    = record,
            onConfirm = { updated ->
                onUpdate(updated)
                editingRecord = null
            },
            onDismiss = { editingRecord = null }
        )
    }

    Card(
        modifier = Modifier.fillMaxWidth(),
        colors   = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
        )
    ) {
        Column {
            // ヘッダー（タップで開閉）
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable { expanded = !expanded }
                    .padding(horizontal = 12.dp, vertical = 10.dp),
                verticalAlignment   = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Row(
                    verticalAlignment  = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    Icon(
                        Icons.Default.List, null,
                        modifier = Modifier.size(18.dp),
                        tint     = MaterialTheme.colorScheme.primary
                    )
                    Text(
                        "採集済み標本一覧（${specimens.size}点）",
                        style      = MaterialTheme.typography.titleSmall,
                        fontWeight = FontWeight.SemiBold
                    )
                }
                Icon(
                    if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
                    null,
                    modifier = Modifier.size(18.dp),
                    tint     = MaterialTheme.colorScheme.outline
                )
            }

            // 展開時: 標本カード一覧
            if (expanded) {
                HorizontalDivider(modifier = Modifier.padding(horizontal = 12.dp))
                Column(
                    modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    specimens.forEach { specimen ->
                        SpecimenSummaryCard(
                            specimen  = specimen,
                            onEdit    = { editingRecord = specimen }
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun SpecimenSummaryCard(
    specimen: com.fieldlabel.collector.domain.model.CollectionRecord,
    onEdit:   () -> Unit
) {
    Surface(
        shape    = MaterialTheme.shapes.small,
        tonalElevation = 3.dp,
        modifier = Modifier.fillMaxWidth()
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 12.dp, vertical = 8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
                Text(
                    "標本 ${specimen.specimenNumber}  (${specimen.specimenId})",
                    style      = MaterialTheme.typography.labelMedium,
                    fontWeight = FontWeight.SemiBold,
                    color      = MaterialTheme.colorScheme.primary
                )
                val coordText = "${"%.5f".format(specimen.latitude)}°N, ${"%.5f".format(specimen.longitude)}°E"
                Text(
                    specimen.collectionDate.toString() + "  " + coordText,
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurface
                )
                if (specimen.notes.isNotBlank()) {
                    Text(
                        specimen.notes,
                        style    = MaterialTheme.typography.bodySmall,
                        color    = MaterialTheme.colorScheme.outline,
                        maxLines = 2,
                        overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
                    )
                }
            }
            IconButton(onClick = onEdit) {
                Icon(
                    Icons.Default.Edit, "編集",
                    modifier = Modifier.size(18.dp),
                    tint     = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// 標本編集ダイアログ
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun SpecimenEditDialog(
    record:    com.fieldlabel.collector.domain.model.CollectionRecord,
    onConfirm: (com.fieldlabel.collector.domain.model.CollectionRecord) -> Unit,
    onDismiss: () -> Unit
) {
    var notes      by remember { mutableStateOf(record.notes) }
    var dateStr    by remember { mutableStateOf(record.collectionDate.toString()) }
    var latStr     by remember { mutableStateOf("%.6f".format(record.latitude)) }
    var lonStr     by remember { mutableStateOf("%.6f".format(record.longitude)) }
    var altStr     by remember { mutableStateOf(record.altitude?.let { "%.1f".format(it) } ?: "") }
    var addressStr by remember { mutableStateOf(record.address ?: "") }

    var dateError  by remember { mutableStateOf(false) }
    var coordError by remember { mutableStateOf(false) }

    AlertDialog(
        onDismissRequest = onDismiss,
        title = {
            Column {
                Text("標本 ${record.specimenNumber} を編集")
                Text(
                    record.specimenId,
                    style = MaterialTheme.typography.labelSmall,
                    color = MaterialTheme.colorScheme.outline
                )
            }
        },
        text = {
            Column(
                modifier            = Modifier
                    .fillMaxWidth()
                    .verticalScroll(rememberScrollState()),
                verticalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                // 備考
                OutlinedTextField(
                    value         = notes,
                    onValueChange = { notes = it },
                    label         = { Text("備考・メモ") },
                    leadingIcon   = { Icon(Icons.Default.Notes, null) },
                    minLines      = 2,
                    maxLines      = 4,
                    modifier      = Modifier.fillMaxWidth()
                )

                HorizontalDivider()

                // 採集日
                OutlinedTextField(
                    value         = dateStr,
                    onValueChange = {
                        dateStr   = it
                        dateError = runCatching { java.time.LocalDate.parse(it) }.isFailure
                    },
                    label         = { Text("採集日 (YYYY-MM-DD)") },
                    leadingIcon   = { Icon(Icons.Default.CalendarToday, null) },
                    isError       = dateError,
                    supportingText = if (dateError) ({ Text("形式: 2026-05-20") }) else null,
                    singleLine    = true,
                    modifier      = Modifier.fillMaxWidth()
                )

                HorizontalDivider()

                // 座標
                Text("GPS座標", style = MaterialTheme.typography.labelMedium,
                    color = MaterialTheme.colorScheme.outline)
                Row(
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    OutlinedTextField(
                        value         = latStr,
                        onValueChange = {
                            latStr     = it
                            coordError = latStr.toDoubleOrNull() == null || lonStr.toDoubleOrNull() == null
                        },
                        label         = { Text("緯度 (°N)") },
                        isError       = coordError,
                        singleLine    = true,
                        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
                        modifier      = Modifier.weight(1f)
                    )
                    OutlinedTextField(
                        value         = lonStr,
                        onValueChange = {
                            lonStr     = it
                            coordError = latStr.toDoubleOrNull() == null || lonStr.toDoubleOrNull() == null
                        },
                        label         = { Text("経度 (°E)") },
                        isError       = coordError,
                        singleLine    = true,
                        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
                        modifier      = Modifier.weight(1f)
                    )
                }

                // 標高
                OutlinedTextField(
                    value         = altStr,
                    onValueChange = { altStr = it },
                    label         = { Text("標高 (m)") },
                    singleLine    = true,
                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
                    modifier      = Modifier.fillMaxWidth()
                )

                HorizontalDivider()

                // 住所
                OutlinedTextField(
                    value         = addressStr,
                    onValueChange = { addressStr = it },
                    label         = { Text("住所・場所") },
                    leadingIcon   = { Icon(Icons.Default.Place, null) },
                    minLines      = 2,
                    maxLines      = 3,
                    modifier      = Modifier.fillMaxWidth()
                )
            }
        },
        confirmButton = {
            TextButton(
                enabled = !dateError && !coordError,
                onClick = {
                    val parsedDate = runCatching { java.time.LocalDate.parse(dateStr) }.getOrNull()
                        ?: return@TextButton
                    val lat = latStr.toDoubleOrNull() ?: return@TextButton
                    val lon = lonStr.toDoubleOrNull() ?: return@TextButton
                    val alt = altStr.toDoubleOrNull()
                    onConfirm(
                        record.copy(
                            notes          = notes,
                            collectionDate = parsedDate,
                            latitude       = lat,
                            longitude      = lon,
                            altitude       = alt,
                            address        = addressStr.ifBlank { null },
                            synced         = false
                        )
                    )
                }
            ) { Text("保存") }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) { Text("キャンセル") }
        }
    )
}

// ─────────────────────────────────────────────────────────────────────────────
// 写真タグ付けダイアログ
// ─────────────────────────────────────────────────────────────────────────────
@Composable
private fun PhotoTagDialog(
    photoFile:  File,
    session:    LocationSession,
    initialTag: String? = null,    // null = 地点全体
    onConfirm:  (specimenId: String?) -> Unit,
    onDismiss:  () -> Unit
) {
    var selectedTag by remember(initialTag) { mutableStateOf(initialTag) }

    AlertDialog(
        onDismissRequest = onDismiss,
        title  = { Text("写真を紐づける") },
        text   = {
            Column(
                modifier            = Modifier.fillMaxWidth(),
                verticalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                // プレビュー（固定）
                AsyncImage(
                    model              = photoFile,
                    contentDescription = null,
                    contentScale       = ContentScale.Fit,
                    modifier           = Modifier
                        .fillMaxWidth()
                        .height(140.dp)
                        .clip(MaterialTheme.shapes.medium)
                )

                Text(
                    "紐づけるラベルを選択",
                    style = MaterialTheme.typography.labelMedium
                )

                // ── スクロール可能な選択リスト ────────────────────────────
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .heightIn(max = 260.dp)
                        .verticalScroll(rememberScrollState()),
                    verticalArrangement = Arrangement.spacedBy(0.dp)
                ) {
                    // 地点全体オプション
                    Row(
                        modifier          = Modifier
                            .fillMaxWidth()
                            .clickable { selectedTag = null },
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        RadioButton(selected = selectedTag == null, onClick = { selectedTag = null })
                        Spacer(Modifier.width(4.dp))
                        Column {
                            Text("地点全体", style = MaterialTheme.typography.bodyMedium)
                            Text(session.locationId,
                                style = MaterialTheme.typography.labelSmall,
                                color = MaterialTheme.colorScheme.outline)
                        }
                    }

                    // 採集済み標本
                    if (session.nextSpecimenNumber > 1) {
                        HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
                        for (i in 1 until session.nextSpecimenNumber) {
                            val specId = "${session.locationId}-%02d".format(i)
                            Row(
                                modifier          = Modifier
                                    .fillMaxWidth()
                                    .clickable { selectedTag = specId },
                                verticalAlignment = Alignment.CenterVertically
                            ) {
                                RadioButton(
                                    selected = selectedTag == specId,
                                    onClick  = { selectedTag = specId }
                                )
                                Spacer(Modifier.width(4.dp))
                                Column {
                                    Text("標本 $i", style = MaterialTheme.typography.bodyMedium)
                                    Text(specId,
                                        style = MaterialTheme.typography.labelSmall,
                                        color = MaterialTheme.colorScheme.outline)
                                }
                            }
                        }
                    }
                }
            }
        },
        confirmButton = {
            TextButton(onClick = { onConfirm(selectedTag) }) { Text("保存") }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) { Text("キャンセル") }
        }
    )
}
