// =============================================================
// history_page.dart
//
// Dependensi yang perlu ditambahkan di pubspec.yaml:
//   firebase_database: ^11.0.0   (sudah dipakai di home_page.dart)
//   fl_chart: ^0.68.0            (grafik di dalam aplikasi)
//   pdf: ^3.10.0                 (membuat file PDF)
//   printing: ^5.12.0            (preview / share / simpan PDF)
//   intl: ^0.19.0                (format tanggal & jam)
//
// Struktur data Firebase yang diasumsikan:
// riwayat_pengeringan/
//   -sessionId/
//       waktu_mulai   : "2026-07-08T08:00:00"
//       waktu_selesai : "2026-07-08T14:30:00"
//       logs/
//         -logId: { time: "2026-07-08T08:05:00", suhu: 45.2, kelembapan: 60.1 }
//
// Sesuaikan nama field di DryingSession.fromSnapshot() /
// DryingLog.fromMap() kalau struktur di project kamu berbeda.
//
// FIX LocaleDataException:
// DateFormat(..., 'id_ID') butuh data locale Indonesia di-load
// dulu sebelum dipakai. Supaya file ini tidak bergantung ke
// main.dart (dan tidak error walau kamu lupa setup di sana),
// initializeDateFormatting('id_ID', null) dipanggil sendiri di
// initState() HistoryPage, SEBELUM data di-fetch / halaman detail
// dibuka. Karena ini inisialisasi global sekali untuk seluruh app,
// SessionDetailPage yang hanya bisa diakses lewat HistoryPage juga
// otomatis aman.
//
// CATATAN PERUBAHAN TERAKHIR:
// - Kartu "Suhu Rata-rata" dan "Kelembapan Awal -> Akhir" di
//   halaman detail dihapus.
// - Sebagai gantinya, "Suhu Min-Max" dan "Kelembapan Min-Max"
//   sekarang ditampilkan sebagai kartu besar (gaya sensorCard),
//   bukan lagi baris kecil di bawah.
// =============================================================

import 'dart:math';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';

/// =========================================================
/// MODEL: satu titik data suhu & kelembapan pada waktu tertentu
/// =========================================================
class DryingLog {
  final DateTime time;
  final double suhu;
  final double kelembapan;

  DryingLog({
    required this.time,
    required this.suhu,
    required this.kelembapan,
  });

  factory DryingLog.fromMap(Map<dynamic, dynamic> map) {
    final rawTime = map['time'] ?? map['waktu'];
    DateTime parsedTime;
    if (rawTime is int) {
      parsedTime = DateTime.fromMillisecondsSinceEpoch(rawTime);
    } else {
      parsedTime = DateTime.tryParse(rawTime.toString()) ?? DateTime.now();
    }

    return DryingLog(
      time: parsedTime,
      suhu: (map['suhu'] as num?)?.toDouble() ?? 0,
      kelembapan: (map['kelembapan'] as num?)?.toDouble() ?? 0,
    );
  }
}

/// =========================================================
/// MODEL: satu sesi / rekap pengeringan
/// =========================================================
class DryingSession {
  final String id;
  final DateTime startTime;
  final DateTime? endTime;
  final List<DryingLog> logs;

  DryingSession({
    required this.id,
    required this.startTime,
    required this.endTime,
    required this.logs,
  });

  Duration get duration => (endTime ?? DateTime.now()).difference(startTime);

  double get avgSuhu => logs.isEmpty
      ? 0
      : logs.map((e) => e.suhu).reduce((a, b) => a + b) / logs.length;

  double get avgKelembapan => logs.isEmpty
      ? 0
      : logs.map((e) => e.kelembapan).reduce((a, b) => a + b) / logs.length;

  double get maxSuhu => logs.isEmpty ? 0 : logs.map((e) => e.suhu).reduce(max);
  double get minSuhu => logs.isEmpty ? 0 : logs.map((e) => e.suhu).reduce(min);
  double get maxKelembapan =>
      logs.isEmpty ? 0 : logs.map((e) => e.kelembapan).reduce(max);
  double get minKelembapan =>
      logs.isEmpty ? 0 : logs.map((e) => e.kelembapan).reduce(min);

  // ===== Kelembapan awal -> akhir =====
  // logs sudah terurut ascending berdasarkan waktu (lihat
  // fromSnapshot di bawah), jadi first = titik data paling awal,
  // last = titik data paling akhir dalam sesi ini.
  double get startKelembapan => logs.isEmpty ? 0 : logs.first.kelembapan;
  double get endKelembapan => logs.isEmpty ? 0 : logs.last.kelembapan;

  factory DryingSession.fromSnapshot(String id, Map<dynamic, dynamic> map) {
    DateTime parseDate(dynamic raw) {
      if (raw == null) return DateTime.now();
      if (raw is int) return DateTime.fromMillisecondsSinceEpoch(raw);
      return DateTime.tryParse(raw.toString()) ?? DateTime.now();
    }

    final start = parseDate(map['waktu_mulai'] ?? map['start_time']);
    final endRaw = map['waktu_selesai'] ?? map['end_time'];
    final end = endRaw == null ? null : parseDate(endRaw);

    final List<DryingLog> logs = [];
    final rawLogs = map['logs'];

    if (rawLogs is Map) {
      rawLogs.forEach((_, v) {
        try {
          logs.add(DryingLog.fromMap(Map<dynamic, dynamic>.from(v)));
        } catch (_) {}
      });
    } else if (rawLogs is List) {
      for (final v in rawLogs) {
        if (v == null) continue;
        try {
          logs.add(DryingLog.fromMap(Map<dynamic, dynamic>.from(v)));
        } catch (_) {}
      }
    }

    logs.sort((a, b) => a.time.compareTo(b.time));

    return DryingSession(id: id, startTime: start, endTime: end, logs: logs);
  }
}

/// =========================================================
/// HALAMAN: daftar riwayat pengeringan
/// =========================================================
class HistoryPage extends StatefulWidget {
  const HistoryPage({super.key});

  @override
  State<HistoryPage> createState() => _HistoryPageState();
}

class _HistoryPageState extends State<HistoryPage> {
  final dbRef = FirebaseDatabase.instance.ref("riwayat_pengeringan");

  List<DryingSession> sessions = [];
  bool loading = true;
  bool downloadingId = false;

  // Supaya initializeDateFormatting tidak dipanggil berulang kali
  // kalau HistoryPage dibuka-tutup berkali-kali dalam 1 sesi app.
  static bool _localeReady = false;

  @override
  void initState() {
    super.initState();
    _initLocaleAndFetch();
  }

  /// FIX LocaleDataException: pastikan data locale 'id_ID' sudah
  /// ter-load SEBELUM ada DateFormat(..., 'id_ID') yang dipanggil
  /// di mana pun pada halaman ini (termasuk SessionDetailPage).
  Future<void> _initLocaleAndFetch() async {
    if (!_localeReady) {
      await initializeDateFormatting('id_ID', null);
      _localeReady = true;
    }

    await fetchHistory();
  }

  Future<void> fetchHistory() async {
    setState(() => loading = true);
    try {
      final snapshot = await dbRef.get();
      final List<DryingSession> result = [];

      if (snapshot.exists && snapshot.value != null) {
        final map = Map<dynamic, dynamic>.from(snapshot.value as Map);
        map.forEach((key, value) {
          try {
            result.add(
              DryingSession.fromSnapshot(
                key.toString(),
                Map<dynamic, dynamic>.from(value),
              ),
            );
          } catch (e) {
            debugPrint("ERROR PARSE SESSION $key: $e");
          }
        });
      }

      result.sort((a, b) => b.startTime.compareTo(a.startTime));

      setState(() {
        sessions = result;
        loading = false;
      });
    } catch (e) {
      debugPrint("ERROR FETCH HISTORY: $e");
      setState(() => loading = false);
    }
  }

  Future<void> _quickDownload(DryingSession session) async {
    setState(() => downloadingId = true);
    try {
      final bytes = await PdfReportGenerator.generate(session);
      final fileName =
          'laporan_pengeringan_${DateFormat('yyyyMMdd_HHmm').format(session.startTime)}.pdf';
      await Printing.sharePdf(bytes: bytes, filename: fileName);
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text("Gagal membuat PDF: $e")),
        );
      }
    } finally {
      if (mounted) setState(() => downloadingId = false);
    }
  }

  /// Tampilkan dialog konfirmasi sebelum menghapus 1 sesi riwayat.
  Future<void> _confirmDeleteSession(DryingSession session) async {
    final dateFmt = DateFormat('dd MMM yyyy', 'id_ID');

    final confirmed = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20),
        ),
        title: const Text(
          "Hapus Riwayat?",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        content: Text(
          "Riwayat pengeringan tanggal ${dateFmt.format(session.startTime)} "
          "beserta seluruh data suhu & kelembapannya akan dihapus permanen "
          "dan tidak bisa dikembalikan.",
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text("Batal"),
          ),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.red,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(12),
              ),
            ),
            onPressed: () => Navigator.pop(context, true),
            child: const Text(
              "Hapus",
              style: TextStyle(color: Colors.white),
            ),
          ),
        ],
      ),
    );

    if (confirmed == true) {
      await _deleteSession(session);
    }
  }

  /// Hapus 1 sesi riwayat (beserta seluruh logs di dalamnya, karena
  /// dihapus dari node induknya) dari Firebase, lalu update tampilan.
  Future<void> _deleteSession(DryingSession session) async {
    try {
      await dbRef.child(session.id).remove();

      if (mounted) {
        setState(() {
          sessions.removeWhere((s) => s.id == session.id);
        });

        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text("Riwayat berhasil dihapus")),
        );
      }
    } catch (e) {
      debugPrint("ERROR DELETE SESSION: $e");

      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text("Gagal menghapus riwayat: $e")),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF6F1EE),
      body: SafeArea(
        child: RefreshIndicator(
          onRefresh: fetchHistory,
          child: SingleChildScrollView(
            physics: const AlwaysScrollableScrollPhysics(),
            padding: const EdgeInsets.all(20),
            child: Column(
              children: [
                /// HEADER (senada dengan HomePage)
                Container(
                  padding: const EdgeInsets.all(20),
                  decoration: BoxDecoration(
                    gradient: const LinearGradient(
                      colors: [Color(0xFF6F4E37), Color(0xFF8D6E63)],
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                    ),
                    borderRadius: BorderRadius.circular(30),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.brown.withOpacity(0.2),
                        blurRadius: 15,
                        offset: const Offset(0, 8),
                      ),
                    ],
                  ),
                  child: Row(
                    children: [
                      if (Navigator.of(context).canPop())
                        Padding(
                          padding: const EdgeInsets.only(right: 10),
                          child: InkWell(
                            borderRadius: BorderRadius.circular(30),
                            onTap: () => Navigator.of(context).maybePop(),
                            child: const Icon(
                              Icons.arrow_back_ios_new,
                              color: Colors.white,
                              size: 20,
                            ),
                          ),
                        ),
                      Container(
                        padding: const EdgeInsets.all(14),
                        decoration: BoxDecoration(
                          color: Colors.white.withOpacity(0.2),
                          shape: BoxShape.circle,
                        ),
                        child: const Icon(
                          Icons.history,
                          color: Colors.white,
                          size: 30,
                        ),
                      ),
                      const SizedBox(width: 15),
                      const Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              "Riwayat Pengeringan",
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 24,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                            SizedBox(height: 5),
                            Text(
                              "Rekap Suhu & Kelembapan Kopi",
                              style: TextStyle(
                                color: Colors.white70,
                                fontSize: 14,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),

                const SizedBox(height: 25),

                /// RINGKASAN JUMLAH SESI (gaya sama dengan STATUS MODE)
                Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 20,
                    vertical: 15,
                  ),
                  decoration: BoxDecoration(
                    color: Colors.brown.shade400,
                    borderRadius: BorderRadius.circular(20),
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      const Icon(Icons.coffee_maker, color: Colors.white),
                      const SizedBox(width: 10),
                      Text(
                        loading
                            ? "MEMUAT DATA..."
                            : "${sessions.length} SESI TERCATAT",
                        style: const TextStyle(
                          color: Colors.white,
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          letterSpacing: 1,
                        ),
                      ),
                    ],
                  ),
                ),

                const SizedBox(height: 25),

                /// DAFTAR RIWAYAT
                Align(
                  alignment: Alignment.centerLeft,
                  child: Text(
                    "Daftar Sesi",
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                      color: Colors.brown.shade800,
                    ),
                  ),
                ),

                const SizedBox(height: 15),

                if (loading)
                  const Padding(
                    padding: EdgeInsets.only(top: 40),
                    child: CircularProgressIndicator(),
                  )
                else if (sessions.isEmpty)
                  _emptyState()
                else
                  Column(
                    children: sessions
                        .map((session) => _sessionCard(session))
                        .toList(),
                  ),

                const SizedBox(height: 20),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _emptyState() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.symmetric(vertical: 40),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(25),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 15,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.history_toggle_off, size: 50, color: Colors.brown.shade200),
          const SizedBox(height: 14),
          Text(
            "Belum ada riwayat pengeringan",
            style: TextStyle(color: Colors.brown.shade400, fontSize: 14),
          ),
        ],
      ),
    );
  }

  /// KARTU SESI - gaya sama dengan sensorCard/buildControlCard di HomePage
  Widget _sessionCard(DryingSession session) {
    final dateFmt = DateFormat('dd MMM yyyy', 'id_ID');
    final duration = session.duration;
    final jam = duration.inHours;
    final menit = duration.inMinutes % 60;

    return Container(
      margin: const EdgeInsets.only(bottom: 15),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(25),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 15,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Material(
        color: Colors.transparent,
        borderRadius: BorderRadius.circular(25),
        child: InkWell(
          borderRadius: BorderRadius.circular(25),
          onTap: () async {
            final deleted = await Navigator.push<bool>(
              context,
              MaterialPageRoute(
                builder: (_) => SessionDetailPage(session: session),
              ),
            );

            // Kalau sesi dihapus dari halaman detail, sinkronkan
            // daftar di halaman ini juga.
            if (deleted == true && mounted) {
              setState(() {
                sessions.removeWhere((s) => s.id == session.id);
              });
            }
          },
          child: Padding(
            padding: const EdgeInsets.all(18),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  children: [
                    Container(
                      padding: const EdgeInsets.all(14),
                      decoration: BoxDecoration(
                        color: Colors.brown.withOpacity(0.1),
                        shape: BoxShape.circle,
                      ),
                      child: const Icon(Icons.coffee, color: Colors.brown),
                    ),
                    const SizedBox(width: 15),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            dateFmt.format(session.startTime),
                            style: const TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 16,
                            ),
                          ),
                          const SizedBox(height: 4),
                          Text(
                            "Lama pengeringan: $jam jam $menit menit",
                            style: TextStyle(
                              fontSize: 12,
                              color: Colors.grey.shade600,
                            ),
                          ),
                        ],
                      ),
                    ),
                    IconButton(
                      tooltip: "Unduh PDF",
                      onPressed:
                          downloadingId ? null : () => _quickDownload(session),
                      icon: const Icon(
                        Icons.download_rounded,
                        color: Colors.brown,
                      ),
                    ),
                    IconButton(
                      tooltip: "Hapus",
                      onPressed: () => _confirmDeleteSession(session),
                      icon: const Icon(
                        Icons.delete_outline,
                        color: Colors.red,
                      ),
                    ),
                    Icon(Icons.chevron_right, color: Colors.grey.shade400),
                  ],
                ),
                const SizedBox(height: 14),
                Row(
                  children: [
                    Expanded(
                      child: _miniStat(
                        icon: Icons.thermostat,
                        color: Colors.redAccent,
                        label:
                            "${session.avgSuhu.toStringAsFixed(1)}°C rata-rata",
                      ),
                    ),
                    const SizedBox(width: 10),
                    Expanded(
                      child: _miniStat(
                        icon: Icons.water_drop,
                        color: Colors.blue,
                        label:
                            "${session.startKelembapan.toStringAsFixed(1)}% → ${session.endKelembapan.toStringAsFixed(1)}%",
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _miniStat({
    required IconData icon,
    required Color color,
    required String label,
  }) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
      decoration: BoxDecoration(
        color: color.withOpacity(0.1),
        borderRadius: BorderRadius.circular(15),
      ),
      child: Row(
        children: [
          Icon(icon, size: 18, color: color),
          const SizedBox(width: 8),
          Expanded(
            child: Text(
              label,
              style: TextStyle(
                fontSize: 12,
                fontWeight: FontWeight.w600,
                color: color,
              ),
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ],
      ),
    );
  }
}

/// =========================================================
/// HALAMAN: detail satu sesi pengeringan (grafik + tabel + PDF)
/// =========================================================
class SessionDetailPage extends StatefulWidget {
  final DryingSession session;
  const SessionDetailPage({super.key, required this.session});

  @override
  State<SessionDetailPage> createState() => _SessionDetailPageState();
}

class _SessionDetailPageState extends State<SessionDetailPage> {
  bool generating = false;

  Future<void> _previewPdf() async {
    await Printing.layoutPdf(
      onLayout: (format) => PdfReportGenerator.generate(widget.session),
      name: 'laporan_pengeringan_'
          '${DateFormat('yyyyMMdd_HHmm').format(widget.session.startTime)}',
    );
  }

  Future<void> _downloadPdf() async {
    setState(() => generating = true);
    try {
      final bytes = await PdfReportGenerator.generate(widget.session);
      final fileName = 'laporan_pengeringan_'
          '${DateFormat('yyyyMMdd_HHmm').format(widget.session.startTime)}.pdf';
      await Printing.sharePdf(bytes: bytes, filename: fileName);
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text("Gagal membuat PDF: $e")),
        );
      }
    } finally {
      if (mounted) setState(() => generating = false);
    }
  }

  bool deleting = false;

  /// Tampilkan dialog konfirmasi, lalu hapus sesi ini dari Firebase.
  /// Kalau berhasil, halaman ditutup dan mengembalikan `true` supaya
  /// HistoryPage tahu harus menghapus sesi ini juga dari daftarnya.
  Future<void> _confirmAndDelete() async {
    final dateFmt = DateFormat('dd MMM yyyy', 'id_ID');

    final confirmed = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20),
        ),
        title: const Text(
          "Hapus Riwayat?",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        content: Text(
          "Riwayat pengeringan tanggal ${dateFmt.format(widget.session.startTime)} "
          "beserta seluruh data suhu & kelembapannya akan dihapus permanen "
          "dan tidak bisa dikembalikan.",
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text("Batal"),
          ),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.red,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(12),
              ),
            ),
            onPressed: () => Navigator.pop(context, true),
            child: const Text(
              "Hapus",
              style: TextStyle(color: Colors.white),
            ),
          ),
        ],
      ),
    );

    if (confirmed != true) return;

    setState(() => deleting = true);

    try {
      await FirebaseDatabase.instance
          .ref("riwayat_pengeringan")
          .child(widget.session.id)
          .remove();

      if (mounted) {
        Navigator.pop(context, true);
      }
    } catch (e) {
      debugPrint("ERROR DELETE SESSION: $e");

      if (mounted) {
        setState(() => deleting = false);

        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text("Gagal menghapus riwayat: $e")),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final session = widget.session;
    final dateFmt = DateFormat('dd MMMM yyyy', 'id_ID');
    final timeFmt = DateFormat('HH:mm');
    final duration = session.duration;
    final jam = duration.inHours;
    final menit = duration.inMinutes % 60;

    return Scaffold(
      backgroundColor: const Color(0xFFF6F1EE),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(20),
          child: Column(
            children: [
              /// HEADER (senada dengan HomePage)
              Container(
                padding: const EdgeInsets.all(20),
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    colors: [Color(0xFF6F4E37), Color(0xFF8D6E63)],
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                  ),
                  borderRadius: BorderRadius.circular(30),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.brown.withOpacity(0.2),
                      blurRadius: 15,
                      offset: const Offset(0, 8),
                    ),
                  ],
                ),
                child: Row(
                  children: [
                    InkWell(
                      borderRadius: BorderRadius.circular(30),
                      onTap: () => Navigator.of(context).maybePop(),
                      child: const Padding(
                        padding: EdgeInsets.only(right: 10),
                        child: Icon(
                          Icons.arrow_back_ios_new,
                          color: Colors.white,
                          size: 20,
                        ),
                      ),
                    ),
                    Container(
                      padding: const EdgeInsets.all(14),
                      decoration: BoxDecoration(
                        color: Colors.white.withOpacity(0.2),
                        shape: BoxShape.circle,
                      ),
                      child: const Icon(
                        Icons.receipt_long,
                        color: Colors.white,
                        size: 30,
                      ),
                    ),
                    const SizedBox(width: 15),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            dateFmt.format(session.startTime),
                            style: const TextStyle(
                              color: Colors.white,
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 5),
                          Text(
                            "${timeFmt.format(session.startTime)} - "
                            "${session.endTime != null ? timeFmt.format(session.endTime!) : '-'}"
                            "  •  $jam jam $menit menit",
                            style: const TextStyle(
                              color: Colors.white70,
                              fontSize: 13,
                            ),
                          ),
                        ],
                      ),
                    ),
                    InkWell(
                      borderRadius: BorderRadius.circular(30),
                      onTap: deleting ? null : _confirmAndDelete,
                      child: Padding(
                        padding: const EdgeInsets.only(left: 10),
                        child: deleting
                            ? const SizedBox(
                                width: 20,
                                height: 20,
                                child: CircularProgressIndicator(
                                  strokeWidth: 2,
                                  color: Colors.white,
                                ),
                              )
                            : const Icon(
                                Icons.delete_outline,
                                color: Colors.white,
                                size: 24,
                              ),
                      ),
                    ),
                  ],
                ),
              ),

              const SizedBox(height: 25),

              /// SUHU MIN-MAX & KELEMBAPAN MIN-MAX (kartu besar, gaya
              /// sama dengan sensorCard - menggantikan kartu Suhu
              /// Rata-rata & Kelembapan Awal->Akhir yang dihapus)
              Row(
                children: [
                  Expanded(
                    child: sensorCard(
                      title: "Suhu Min - Max",
                      value:
                          "${session.minSuhu.toStringAsFixed(1)}° - ${session.maxSuhu.toStringAsFixed(1)}°",
                      icon: Icons.thermostat,
                      color: Colors.redAccent,
                    ),
                  ),
                  const SizedBox(width: 15),
                  Expanded(
                    child: sensorCard(
                      title: "Kelembapan Min - Max",
                      value:
                          "${session.minKelembapan.toStringAsFixed(1)}% - ${session.maxKelembapan.toStringAsFixed(1)}%",
                      icon: Icons.water_drop,
                      color: Colors.blue,
                    ),
                  ),
                ],
              ),

              const SizedBox(height: 30),

              /// UNDUH LAPORAN (gaya sama dengan Kontrol Mesin)
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  "Unduh Laporan",
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    color: Colors.brown.shade800,
                  ),
                ),
              ),
              const SizedBox(height: 15),
              Row(
                children: [
                  Expanded(
                    child: controlButton(
                      title: "Preview PDF",
                      icon: Icons.picture_as_pdf_outlined,
                      color: Colors.blueGrey,
                      onTap: _previewPdf,
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: controlButton(
                      title: generating ? "Membuat..." : "Unduh PDF",
                      icon: Icons.download_rounded,
                      color: Colors.brown.shade700,
                      onTap: generating ? () {} : _downloadPdf,
                    ),
                  ),
                ],
              ),

              const SizedBox(height: 30),

              /// GRAFIK
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  "Grafik Suhu & Kelembapan",
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    color: Colors.brown.shade800,
                  ),
                ),
              ),
              const SizedBox(height: 15),
              Container(
                width: double.infinity,
                padding: const EdgeInsets.fromLTRB(8, 20, 20, 12),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(25),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.05),
                      blurRadius: 15,
                      offset: const Offset(0, 8),
                    ),
                  ],
                ),
                child: Column(
                  children: [
                    _legend(),
                    const SizedBox(height: 10),
                    SizedBox(height: 240, child: _buildChart(session)),
                  ],
                ),
              ),

              const SizedBox(height: 30),

              /// REKAP DATA - ringkasan sesi dalam bentuk tabel
              /// label:nilai (gaya sama seperti ringkasan di laporan
              /// PDF), menggantikan tabel mentah semua titik data yang
              /// sebelumnya ada di sini.
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  "Rekap Data",
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    color: Colors.brown.shade800,
                  ),
                ),
              ),
              const SizedBox(height: 15),
              recapDataCard(session, timeFmt),

              const SizedBox(height: 30),

              /// KESIMPULAN SESI - gaya sama dengan kartu "Apa Arti
              /// Status Ini?" di HomePage (kartu putih rounded, ikon
              /// lingkaran + judul + deskripsi, dipisah garis tipis)
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  "Kesimpulan Sesi",
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    color: Colors.brown.shade800,
                  ),
                ),
              ),
              const SizedBox(height: 15),
              sessionSummaryCard(session),

              const SizedBox(height: 20),
            ],
          ),
        ),
      ),
    );
  }

  /// SENSOR CARD - identik dengan sensorCard() di HomePage
  Widget sensorCard({
    required String title,
    required String value,
    required IconData icon,
    required Color color,
  }) {
    return Container(
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(25),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 15,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Column(
        children: [
          Container(
            padding: const EdgeInsets.all(15),
            decoration: BoxDecoration(
              color: color.withOpacity(0.1),
              shape: BoxShape.circle,
            ),
            child: Icon(icon, size: 35, color: color),
          ),
          const SizedBox(height: 15),
          Text(
            title,
            textAlign: TextAlign.center,
            style: TextStyle(color: Colors.grey.shade700, fontSize: 14),
          ),
          const SizedBox(height: 10),
          FittedBox(
            fit: BoxFit.scaleDown,
            child: Text(
              value,
              style: TextStyle(
                color: color,
                fontSize: 22,
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
        ],
      ),
    );
  }

  /// CONTROL BUTTON - identik dengan controlButton() di HomePage
  Widget controlButton({
    required String title,
    required IconData icon,
    required Color color,
    required VoidCallback onTap,
  }) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: const EdgeInsets.symmetric(vertical: 18),
        decoration: BoxDecoration(
          color: color,
          borderRadius: BorderRadius.circular(20),
          boxShadow: [
            BoxShadow(
              color: color.withOpacity(0.3),
              blurRadius: 10,
              offset: const Offset(0, 5),
            ),
          ],
        ),
        child: Column(
          children: [
            Icon(icon, color: Colors.white, size: 30),
            const SizedBox(height: 8),
            Text(
              title,
              style: const TextStyle(
                color: Colors.white,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        ),
      ),
    );
  }

  /// =========================================================
  /// KARTU KESIMPULAN SESI
  ///
  /// Gaya visualnya SENGAJA disamakan dengan kartu "Apa Arti Status
  /// Ini?" (termGuideCard) di HomePage: kartu putih rounded, ikon
  /// bulat + judul + deskripsi per baris, dipisah garis tipis.
  ///
  /// Isinya kesimpulan otomatis yang dihitung dari data log sesi ini:
  /// - Apakah target kekeringan (<=15%) tercapai
  /// - Seberapa stabil suhunya selama proses
  /// - Apakah durasinya wajar untuk 1 sesi pengeringan kopi
  /// =========================================================
  Widget sessionSummaryCard(DryingSession session) {
    final points = _buildConclusionPoints(session);

    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(25),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 15,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          for (int i = 0; i < points.length; i++) ...[
            conclusionTile(
              icon: points[i].icon,
              color: points[i].color,
              title: points[i].title,
              description: points[i].description,
            ),
            if (i != points.length - 1)
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 14),
                child: Divider(height: 1),
              ),
          ],
        ],
      ),
    );
  }

  /// Hitung poin-poin kesimpulan dari data log sesi. Kalau ada
  /// aspek lain yang mau ditambahkan (mis. jumlah data terlalu
  /// sedikit, dll), cukup tambah 1 blok baru di sini.
  /// Hitung berapa kali arah suhu BERBALIK (dari naik jadi turun, atau
  /// sebaliknya) sepanjang sesi. Selisih yang lebih kecil dari
  /// [ambangNoise] diabaikan supaya noise pembacaan sensor kecil
  /// (mis. 0.1°C) tidak dihitung sebagai "pembalikan arah".
  ///
  /// Kurva yang naik/turun mulus (khas fase pemanasan awal) akan
  /// menghasilkan angka ini RENDAH walau rentang suhunya lebar -
  /// beda dengan suhu yang benar-benar naik-turun berulang (osilasi),
  /// yang akan menghasilkan angka TINGGI.
  int _countTempDirectionChanges(
    List<DryingLog> logs, {
    double ambangNoise = 0.3,
  }) {
    int changes = 0;
    int lastSign = 0;

    for (int i = 1; i < logs.length; i++) {
      final diff = logs[i].suhu - logs[i - 1].suhu;

      if (diff.abs() < ambangNoise) continue;

      final sign = diff > 0 ? 1 : -1;

      if (lastSign != 0 && sign != lastSign) {
        changes++;
      }

      lastSign = sign;
    }

    return changes;
  }

  List<_ConclusionPoint> _buildConclusionPoints(DryingSession session) {
    final points = <_ConclusionPoint>[];

    // ===== 1. Hasil akhir kelembapan =====
    final akhir = session.endKelembapan;
    if (session.logs.isEmpty) {
      points.add(
        _ConclusionPoint(
          icon: Icons.info_outline,
          color: Colors.grey,
          title: "Data Belum Cukup",
          description:
              "Sesi ini belum punya titik data log sama sekali, jadi belum "
              "bisa disimpulkan.",
        ),
      );
      return points;
    }

    if (akhir <= 15) {
      points.add(
        _ConclusionPoint(
          icon: Icons.check_circle,
          color: Colors.green,
          title: "Target Kekeringan Tercapai",
          description:
              "Kelembapan akhir ${akhir.toStringAsFixed(1)}% sudah di bawah "
              "batas 15% - kopi tergolong sudah kering sesuai standar.",
        ),
      );
    } else {
      points.add(
        _ConclusionPoint(
          icon: Icons.warning_amber_rounded,
          color: Colors.orange,
          title: "Belum Mencapai Target Kekeringan",
          description:
              "Kelembapan akhir masih ${akhir.toStringAsFixed(1)}%, di atas "
              "batas 15% - kemungkinan sesi dihentikan manual sebelum kopi "
              "benar-benar kering.",
        ),
      );
    }

    // ===== 2. Konsistensi suhu =====
    //
    // [FIX] Sebelumnya hanya lihat selisih max-min: rentang lebar
    // langsung dicap "berfluktuasi", padahal suhu yang naik BERTAHAP
    // dari dingin ke panas (kurva mulus, khas fase pemanasan awal)
    // juga menghasilkan rentang lebar tapi itu WAJAR, bukan fluktuasi.
    // "Fluktuasi" yang sebenarnya adalah naik-turun BERULANG - itu
    // yang dihitung lewat jumlah pembalikan arah (direction changes).
    final rentangSuhu = session.maxSuhu - session.minSuhu;
    final arahBerubah = _countTempDirectionChanges(session.logs);
    final rasioArahBerubah =
        session.logs.length > 1 ? arahBerubah / session.logs.length : 0.0;

    if (rentangSuhu <= 15) {
      points.add(
        _ConclusionPoint(
          icon: Icons.thermostat,
          color: Colors.redAccent,
          title: "Suhu Relatif Stabil",
          description:
              "Suhu bergerak di rentang ${rentangSuhu.toStringAsFixed(1)}°C "
              "(${session.minSuhu.toStringAsFixed(1)}°C - "
              "${session.maxSuhu.toStringAsFixed(1)}°C) selama proses, "
              "menandakan heater & fan bekerja cukup konsisten.",
        ),
      );
    } else if (rasioArahBerubah <= 0.15) {
      // Rentang lebar tapi arahnya konsisten (jarang berbalik) ->
      // ini kurva naik/turun bertahap yang mulus, bukan fluktuasi.
      final naik = session.logs.last.suhu >= session.logs.first.suhu;

      points.add(
        _ConclusionPoint(
          icon: naik ? Icons.trending_up : Icons.trending_down,
          color: Colors.blue,
          title: naik ? "Suhu Naik Bertahap (Wajar)" : "Suhu Turun Bertahap (Wajar)",
          description:
              "Suhu ${naik ? 'naik' : 'turun'} secara bertahap dari "
              "${session.minSuhu.toStringAsFixed(1)}°C ke "
              "${session.maxSuhu.toStringAsFixed(1)}°C tanpa naik-turun "
              "berulang. Ini wajar terjadi di fase ${naik ? 'pemanasan awal' : 'pendinginan'} "
              "mesin, bukan indikasi alat bermasalah.",
        ),
      );
    } else {
      points.add(
        _ConclusionPoint(
          icon: Icons.thermostat,
          color: Colors.orange,
          title: "Suhu Cukup Berfluktuasi",
          description:
              "Suhu berulang kali naik-turun (bukan sekadar naik/turun "
              "bertahap), dari ${session.minSuhu.toStringAsFixed(1)}°C "
              "sampai ${session.maxSuhu.toStringAsFixed(1)}°C. Kalau ini "
              "tidak diinginkan, cek kondisi heater, fan, atau posisi sensor.",
        ),
      );
    }

    // ===== 3. Durasi pengeringan =====
    final menitTotal = session.duration.inMinutes;
    if (menitTotal < 60) {
      points.add(
        _ConclusionPoint(
          icon: Icons.timer_outlined,
          color: Colors.orange,
          title: "Durasi Sesi Tergolong Singkat",
          description:
              "Sesi ini cuma berlangsung $menitTotal menit - kemungkinan "
              "ini sesi uji coba alat, bukan pengeringan kopi penuh.",
        ),
      );
    } else {
      final jam = session.duration.inHours;
      final sisaMenit = menitTotal % 60;
      points.add(
        _ConclusionPoint(
          icon: Icons.timer_outlined,
          color: Colors.blue,
          title: "Durasi Pengeringan Normal",
          description:
              "Proses berlangsung selama $jam jam $sisaMenit menit, sudah "
              "dalam rentang wajar untuk 1 sesi pengeringan kopi.",
        ),
      );
    }

    return points;
  }

  /// Satu baris kesimpulan: ikon lingkaran + judul + deskripsi.
  /// Sengaja dibuat mirip termGuideTile di HomePage, tapi tanpa
  /// baris ON/OFF karena isinya pernyataan tunggal, bukan status
  /// yang berganti-ganti.
  Widget conclusionTile({
    required IconData icon,
    required Color color,
    required String title,
    required String description,
  }) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Container(
          padding: const EdgeInsets.all(10),
          decoration: BoxDecoration(
            color: color.withOpacity(0.1),
            shape: BoxShape.circle,
          ),
          child: Icon(icon, color: color, size: 22),
        ),
        const SizedBox(width: 14),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                title,
                style: TextStyle(
                  fontSize: 15,
                  fontWeight: FontWeight.bold,
                  color: Colors.brown.shade800,
                ),
              ),
              const SizedBox(height: 4),
              Text(
                description,
                style: TextStyle(fontSize: 12.5, color: Colors.grey.shade700),
              ),
            ],
          ),
        ),
      ],
    );
  }

  /// =========================================================
  /// KARTU REKAP DATA
  ///
  /// Tabel ringkas label : nilai (Waktu Mulai, Waktu Selesai, Lama
  /// Pengeringan, Suhu Rata-rata, Suhu Min/Max, Kelembapan Min/Max,
  /// Jumlah Data) - sama persis dengan ringkasan yang tampil di
  /// laporan PDF, menggantikan tabel mentah semua titik data.
  /// =========================================================
  Widget recapDataCard(DryingSession session, DateFormat timeFmt) {
    final duration = session.duration;
    final jam = duration.inHours;
    final menit = duration.inMinutes % 60;

    final rows = <MapEntry<String, String>>[
      MapEntry("Waktu Mulai", timeFmt.format(session.startTime)),
      MapEntry(
        "Waktu Selesai",
        session.endTime != null ? timeFmt.format(session.endTime!) : "-",
      ),
      MapEntry("Lama Pengeringan", "$jam jam $menit menit"),
      MapEntry("Suhu Rata-rata", "${session.avgSuhu.toStringAsFixed(1)}°C"),
      MapEntry(
        "Suhu Min / Max",
        "${session.minSuhu.toStringAsFixed(1)}°C / ${session.maxSuhu.toStringAsFixed(1)}°C",
      ),
      MapEntry(
        "Kelembapan Min / Max",
        "${session.minKelembapan.toStringAsFixed(1)}% / ${session.maxKelembapan.toStringAsFixed(1)}%",
      ),
      MapEntry("Jumlah Data", "${session.logs.length} titik"),
    ];

    return Container(
      width: double.infinity,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(25),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 15,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Column(
        children: [
          for (int i = 0; i < rows.length; i++) ...[
            _recapRow(rows[i].key, rows[i].value),
            if (i != rows.length - 1)
              const Divider(height: 1, indent: 18, endIndent: 18),
          ],
        ],
      ),
    );
  }

  /// Satu baris "label : nilai" pada kartu Rekap Data.
  Widget _recapRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
      child: Row(
        children: [
          Expanded(
            child: Text(
              label,
              style: TextStyle(fontSize: 13, color: Colors.grey.shade600),
            ),
          ),
          Text(
            value,
            style: TextStyle(
              fontSize: 13,
              fontWeight: FontWeight.w600,
              color: Colors.brown.shade800,
            ),
          ),
        ],
      ),
    );
  }

  Widget _legend() {
    Widget dot(Color c, String label) => Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              width: 10,
              height: 10,
              decoration: BoxDecoration(color: c, shape: BoxShape.circle),
            ),
            const SizedBox(width: 6),
            Text(label, style: const TextStyle(fontSize: 12)),
          ],
        );

    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        dot(Colors.redAccent, "Suhu (°C)"),
        const SizedBox(width: 20),
        dot(Colors.blue, "Kelembapan (%)"),
      ],
    );
  }

  /// Pilih index mana saja di daftar `logs` yang boleh dikasih label
  /// waktu di sumbu-X grafik, dipilih merata sejumlah `maxLabels`.
  ///
  /// Ini sengaja TIDAK memakai `interval` bawaan SideTitles fl_chart,
  /// karena fl_chart punya kebiasaan memaksa selalu menampilkan label
  /// di titik data paling akhir (maxX) SELAIN label-label hasil
  /// perhitungan interval - kalau titik akhir itu jaraknya dekat
  /// dengan label interval sebelumnya, dua-duanya numpuk (persis
  /// masalah "14:51" & "14:56" yang bertabrakan di grafik).
  ///
  /// Dengan memilih sendiri index-nya secara eksplisit dan merata,
  /// jaraknya konsisten terjaga berapa pun panjang sesinya.
  Set<int> _pickLabelIndices(int length, {int maxLabels = 5}) {
    if (length <= maxLabels) {
      return List<int>.generate(length, (i) => i).toSet();
    }

    final step = (length - 1) / (maxLabels - 1);
    final indices = <int>{};

    for (int i = 0; i < maxLabels; i++) {
      indices.add((i * step).round());
    }

    return indices;
  }

  Widget _buildChart(DryingSession session) {
    final logs = session.logs;
    if (logs.isEmpty) {
      return Center(
        child: Text(
          "Belum ada data",
          style: TextStyle(color: Colors.grey.shade500),
        ),
      );
    }

    final labelIndices = _pickLabelIndices(logs.length, maxLabels: 5);

    return LineChart(
      LineChartData(
        minY: 0,
        maxY: 100,
        gridData: const FlGridData(show: true, drawVerticalLine: false),
        borderData: FlBorderData(
          show: true,
          border: Border.all(color: Colors.grey.shade300),
        ),
        titlesData: FlTitlesData(
          topTitles:
              const AxisTitles(sideTitles: SideTitles(showTitles: false)),
          rightTitles:
              const AxisTitles(sideTitles: SideTitles(showTitles: false)),
          leftTitles: AxisTitles(
            sideTitles: SideTitles(
              showTitles: true,
              reservedSize: 34,
              interval: 20,
              getTitlesWidget: (value, meta) => Text(
                value.toInt().toString(),
                style: const TextStyle(fontSize: 10),
              ),
            ),
          ),
          bottomTitles: AxisTitles(
            sideTitles: SideTitles(
              showTitles: true,
              reservedSize: 28,
              interval: 1,
              getTitlesWidget: (value, meta) {
                final idx = value.round();
                if (idx < 0 || idx >= logs.length) return const SizedBox();
                if (!labelIndices.contains(idx)) return const SizedBox();
                return Padding(
                  padding: const EdgeInsets.only(top: 6),
                  child: Text(
                    DateFormat('HH:mm').format(logs[idx].time),
                    style: const TextStyle(fontSize: 10),
                  ),
                );
              },
            ),
          ),
        ),
        lineBarsData: [
          LineChartBarData(
            spots: List.generate(
              logs.length,
              (i) => FlSpot(i.toDouble(), logs[i].suhu),
            ),
            isCurved: true,
            color: Colors.redAccent,
            barWidth: 2.5,
            dotData: const FlDotData(show: false),
          ),
          LineChartBarData(
            spots: List.generate(
              logs.length,
              (i) => FlSpot(i.toDouble(), logs[i].kelembapan),
            ),
            isCurved: true,
            color: Colors.blue,
            barWidth: 2.5,
            dotData: const FlDotData(show: false),
          ),
        ],
      ),
    );
  }

  Widget _tableHeaderRow() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
      child: Row(
        children: [
          Expanded(
            flex: 2,
            child: Text(
              "Waktu",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 12,
                color: Colors.brown.shade700,
              ),
            ),
          ),
          Expanded(
            flex: 2,
            child: Text(
              "Suhu",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 12,
                color: Colors.brown.shade700,
              ),
            ),
          ),
          Expanded(
            flex: 2,
            child: Text(
              "Kelembapan",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 12,
                color: Colors.brown.shade700,
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _tableDataRow(String time, String suhu, String kelembapan) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
      child: Row(
        children: [
          Expanded(
            flex: 2,
            child: Text(time, style: const TextStyle(fontSize: 12)),
          ),
          Expanded(
            flex: 2,
            child: Text(
              suhu,
              style: const TextStyle(fontSize: 12, color: Colors.redAccent),
            ),
          ),
          Expanded(
            flex: 2,
            child: Text(
              kelembapan,
              style: const TextStyle(fontSize: 12, color: Colors.blue),
            ),
          ),
        ],
      ),
    );
  }
}

/// =========================================================
/// MODEL: satu poin kesimpulan pada kartu Kesimpulan Sesi
/// =========================================================
class _ConclusionPoint {
  final IconData icon;
  final Color color;
  final String title;
  final String description;

  _ConclusionPoint({
    required this.icon,
    required this.color,
    required this.title,
    required this.description,
  });
}

/// =========================================================
/// GENERATOR PDF
/// Ringkasan + grafik (pw.Chart bawaan package pdf,
/// tidak perlu screenshot widget) + tabel data lengkap.
/// (tidak diubah - hanya tampilan aplikasi yang diselaraskan)
/// =========================================================
class PdfReportGenerator {
  static Future<Uint8List> generate(DryingSession session) async {
    final doc = pw.Document();

    final dateFormat = DateFormat('dd MMMM yyyy', 'id_ID');
    final timeFormat = DateFormat('HH:mm');

    doc.addPage(
      pw.MultiPage(
        pageFormat: PdfPageFormat.a4,
        margin: const pw.EdgeInsets.all(28),
        header: (context) => pw.Column(
          crossAxisAlignment: pw.CrossAxisAlignment.start,
          children: [
            pw.Text(
              'Laporan Pengeringan Kopi',
              style:
                  pw.TextStyle(fontSize: 20, fontWeight: pw.FontWeight.bold),
            ),
            pw.SizedBox(height: 4),
            pw.Text(
              dateFormat.format(session.startTime),
              style: const pw.TextStyle(fontSize: 12, color: PdfColors.grey700),
            ),
            pw.SizedBox(height: 8),
            pw.Divider(color: PdfColors.grey400),
          ],
        ),
        footer: (context) => pw.Align(
          alignment: pw.Alignment.centerRight,
          child: pw.Text(
            'Halaman ${context.pageNumber} / ${context.pagesCount}',
            style: const pw.TextStyle(fontSize: 9, color: PdfColors.grey600),
          ),
        ),
        build: (context) => [
          _buildSummary(session, timeFormat),
          pw.SizedBox(height: 18),
          pw.Text(
            'Grafik Suhu & Kelembapan',
            style: pw.TextStyle(fontSize: 14, fontWeight: pw.FontWeight.bold),
          ),
          pw.SizedBox(height: 8),
          _buildChart(session),
          pw.SizedBox(height: 20),
          pw.Text(
            'Data Rinci',
            style: pw.TextStyle(fontSize: 14, fontWeight: pw.FontWeight.bold),
          ),
          pw.SizedBox(height: 8),
          _buildTable(session, timeFormat),
        ],
      ),
    );

    return doc.save();
  }

  static pw.Widget _buildSummary(DryingSession session, DateFormat timeFormat) {
    final duration = session.duration;
    final jam = duration.inHours;
    final menit = duration.inMinutes % 60;

    pw.Widget row(String label, String value) => pw.Padding(
          padding: const pw.EdgeInsets.symmetric(vertical: 3),
          child: pw.Row(
            children: [
              pw.SizedBox(
                width: 150,
                child: pw.Text(
                  label,
                  style:
                      const pw.TextStyle(fontSize: 11, color: PdfColors.grey700),
                ),
              ),
              pw.Text(
                value,
                style:
                    pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold),
              ),
            ],
          ),
        );

    return pw.Container(
      width: double.infinity,
      padding: const pw.EdgeInsets.all(14),
      decoration: pw.BoxDecoration(
        border: pw.Border.all(color: PdfColors.grey300),
        borderRadius: const pw.BorderRadius.all(pw.Radius.circular(8)),
      ),
      child: pw.Column(
        crossAxisAlignment: pw.CrossAxisAlignment.start,
        children: [
          row('Waktu Mulai', timeFormat.format(session.startTime)),
          row(
            'Waktu Selesai',
            session.endTime != null ? timeFormat.format(session.endTime!) : '-',
          ),
          row('Lama Pengeringan', '$jam jam $menit menit'),
          row('Suhu Rata-rata', '${session.avgSuhu.toStringAsFixed(1)} °C'),
          row(
            'Suhu Min / Max',
            '${session.minSuhu.toStringAsFixed(1)}°C / ${session.maxSuhu.toStringAsFixed(1)}°C',
          ),
          row(
            'Kelembapan Min / Max',
            '${session.minKelembapan.toStringAsFixed(1)}% / ${session.maxKelembapan.toStringAsFixed(1)}%',
          ),
          row('Jumlah Data', '${session.logs.length} titik'),
        ],
      ),
    );
  }

  static pw.Widget _buildChart(DryingSession session) {
    final logs = session.logs;
    if (logs.isEmpty) {
      return pw.Text(
        'Tidak ada data grafik',
        style: const pw.TextStyle(color: PdfColors.grey600),
      );
    }

    final step = (logs.length / 6).ceil().clamp(1, logs.length);
    final labels = List<String>.generate(logs.length, (i) {
      if (i % step == 0) {
        return DateFormat('HH:mm').format(logs[i].time);
      }
      return '';
    });

    return pw.Container(
      height: 220,
      padding: const pw.EdgeInsets.all(8),
      decoration: pw.BoxDecoration(
        border: pw.Border.all(color: PdfColors.grey300),
        borderRadius: const pw.BorderRadius.all(pw.Radius.circular(8)),
      ),
      child: pw.Chart(
        grid: pw.CartesianGrid(
          xAxis: pw.FixedAxis.fromStrings(
            labels,
            marginStart: 30,
            marginEnd: 30,
          ),
          yAxis: pw.FixedAxis([0, 20, 40, 60, 80, 100], divisions: true),
        ),
        datasets: [
          pw.LineDataSet(
            legend: 'Suhu (°C)',
            drawPoints: false,
            isCurved: true,
            color: PdfColors.red,
            lineWidth: 1.5,
            data: List.generate(
              logs.length,
              (i) => pw.PointChartValue(i.toDouble(), logs[i].suhu),
            ),
          ),
          pw.LineDataSet(
            legend: 'Kelembapan (%)',
            drawPoints: false,
            isCurved: true,
            color: PdfColors.blue,
            lineWidth: 1.5,
            data: List.generate(
              logs.length,
              (i) => pw.PointChartValue(i.toDouble(), logs[i].kelembapan),
            ),
          ),
        ],
      ),
    );
  }

  static pw.Widget _buildTable(DryingSession session, DateFormat timeFormat) {
    final logs = session.logs;
    if (logs.isEmpty) {
      return pw.Text(
        'Tidak ada data',
        style: const pw.TextStyle(color: PdfColors.grey600),
      );
    }

    final data = logs
        .map(
          (log) => [
            timeFormat.format(log.time),
            '${log.suhu.toStringAsFixed(1)} °C',
            '${log.kelembapan.toStringAsFixed(1)} %',
          ],
        )
        .toList();

    // Catatan: pada versi lama package `pdf`, gunakan
    // pw.Table.fromTextArray(...) alih-alih pw.TableHelper.fromTextArray(...)
    return pw.TableHelper.fromTextArray(
      headers: ['Waktu', 'Suhu', 'Kelembapan'],
      data: data,
      headerStyle: pw.TextStyle(
        fontWeight: pw.FontWeight.bold,
        fontSize: 10,
        color: PdfColors.white,
      ),
      headerDecoration: const pw.BoxDecoration(color: PdfColors.brown700),
      cellStyle: const pw.TextStyle(fontSize: 9),
      cellAlignments: {
        0: pw.Alignment.centerLeft,
        1: pw.Alignment.center,
        2: pw.Alignment.center,
      },
      border: null,
      rowDecoration: const pw.BoxDecoration(
        border: pw.Border(
          bottom: pw.BorderSide(color: PdfColors.grey300, width: 0.5),
        ),
      ),
    );
  }
}