import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_doc_scanner/flutter_doc_scanner.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';

import '../Functions.dart';
import '../Providers/app_state_provider.dart';
import '../Styles.dart';
import '../Widgets.dart';

// ─── Backend URL ──────────────────────────────────────────────────────────────
// 192.168.18.181 = this computer's local network IP (same Wi-Fi as device)
// Switch to 'http://10.0.2.2:8000' if using the Android Emulator instead
const String _kBase = 'http://192.168.18.181:8000';


// ═══════════════════════════════════════════════════════════════════════════════
//  AnswerSheetGraderPage
// ═══════════════════════════════════════════════════════════════════════════════
class AnswerSheetGraderPage extends StatefulWidget {
  const AnswerSheetGraderPage({super.key});
  @override
  State<AnswerSheetGraderPage> createState() => _AnswerSheetGraderPageState();
}

class _AnswerSheetGraderPageState extends State<AnswerSheetGraderPage> {
  final _scaffoldKey = GlobalKey<ScaffoldState>();

  String? _answerKeyFileName;
  String? _answerKeyPath;
  List<PlatformFile> _answerSheetFiles = [];

  bool _busy = false;
  bool _isGrading = false;
  String? _errorMessage;

  final List<Map<String, dynamic>> _gradingResults = [];
  bool _hasResults = false;
  double _uploadProgress = 0.0;
  bool _cancelled = false;

  // ── Snackbar shorthand ─────────────────────────────────────────────────────
  void _snack(String msg) =>
      showToast(msg, _scaffoldKey.currentContext ?? context);

  // ── Network guard ──────────────────────────────────────────────────────────
  Future<bool> _checkNet() async {
    if (await hasInternetConnection()) return true;
    _snack('No internet connection.');
    return false;
  }

  // ── Cancel upload ──────────────────────────────────────────────────────────
  void _cancelUpload() {
    if (!mounted) return;
    setState(() {
      _cancelled = true;
      _busy = false;
      _isGrading = false;
      _uploadProgress = 0.0;
    });
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  Upload Source Bottom Sheet
  // ══════════════════════════════════════════════════════════════════════════

  void _showUploadSourceSheet({required bool isAnswerKey}) {
    final media = MediaQuery.of(context).size;
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
      ),
      backgroundColor: cardBg,
      builder: (_) => SafeArea(
        child: Padding(
          padding: EdgeInsets.symmetric(
            horizontal: media.width * 0.06,
            vertical: media.height * 0.025,
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // Handle bar
              Container(
                width: 40,
                height: 4,
                margin: const EdgeInsets.only(bottom: 20),
                decoration: BoxDecoration(
                  color: colorBlack54.withValues(alpha: 0.3),
                  borderRadius: BorderRadius.circular(2),
                ),
              ),
              customText(
                text: isAnswerKey
                    ? 'Upload Answer Key'
                    : 'Upload Answer Sheets',
                context: context,
                fontSize: media.shortestSide * 0.045,
                isBold: true,
                color: colorBlack87,
              ),
              SizedBox(height: media.height * 0.025),

              // ── Gallery / Files button ────────────────────────────────
              _SourceOptionTile(
                icon: Icons.photo_library_rounded,
                label: 'Upload from Gallery',
                color: tealPrimary,
                media: media,
                onTap: () {
                  pop(context);
                  isAnswerKey ? _pickAnswerKey() : _pickAnswerSheets();
                },
              ),
              SizedBox(height: media.height * 0.015),

              // ── Camera / Doc Scanner button ───────────────────────────
              _SourceOptionTile(
                icon: Icons.document_scanner_rounded,
                label: 'Scan with Camera',
                color: tealAccent,
                media: media,
                onTap: () {
                  pop(context);
                  isAnswerKey ? _scanAnswerKey() : _scanAnswerSheets();
                },
              ),
              SizedBox(height: media.height * 0.01),
            ],
          ),
        ),
      ),
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  Document Scanner — saves PDF to Downloads, then registers as PlatformFile
  // ══════════════════════════════════════════════════════════════════════════

  /// Copies a scanned PDF [sourcePath] to the device Downloads folder.
  /// Returns the final saved path, or null on failure.
  Future<String?> _savePdfToDownloads(String sourcePath, String fileName) async {
    try {
      // ML Kit returns a file:// URI; File() needs a plain path.
      final resolvedPath = sourcePath.startsWith('file://')
          ? Uri.parse(sourcePath).toFilePath()
          : sourcePath;

      // On Android, /storage/emulated/0/Download is the public Downloads dir.
      // On iOS we fall back to the app's Documents directory (no public Downloads).
      Directory downloadsDir;
      if (Platform.isAndroid) {
        downloadsDir = Directory('/storage/emulated/0/Download');
        if (!await downloadsDir.exists()) {
          // Fallback: use getExternalStorageDirectory
          final ext = await getExternalStorageDirectory();
          downloadsDir = ext ?? await getApplicationDocumentsDirectory();
        }
      } else {
        downloadsDir = await getApplicationDocumentsDirectory();
      }

      // Avoid overwriting — append timestamp if file already exists
      String finalName = fileName;
      File dest = File('${downloadsDir.path}/$finalName');
      if (await dest.exists()) {
        final stamp = DateTime.now().millisecondsSinceEpoch;
        finalName =
        '${fileName.replaceAll('.pdf', '')}_$stamp.pdf';
        dest = File('${downloadsDir.path}/$finalName');
      }

      await File(resolvedPath).copy(dest.path);
      debugPrint('PDF saved to: ${dest.path}');
      return dest.path;
    } catch (e) {
      debugPrint('Failed to save PDF to Downloads: $e');
      return null;
    }
  }

  /// Shows a dialog when the scanner is unavailable and offers a gallery fallback.
  Future<void> _showScannerUnavailableDialog({required bool isAnswerKey}) async {
    if (!mounted) return;
    await showDialog<void>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: const Text('Scanner Unavailable'),
        content: const Text(
          'Document scanning requires an up-to-date version of Google Play Services, '
          'which is not available on this device or emulator.\n\n'
          'Would you like to pick a file from your gallery instead?',
        ),
        actions: [
          TextButton(
            onPressed: () => pop(ctx),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              pop(ctx);
              if (isAnswerKey) {
                _pickAnswerKey();
              } else {
                _pickAnswerSheets();
              }
            },
            child: const Text('Pick from Gallery'),
          ),
        ],
      ),
    );
  }

  /// Scans a single document → PDF → saves to Downloads → sets as answer key.
  Future<void> _scanAnswerKey() async {
    try {
      final result =
      await FlutterDocScanner().getScannedDocumentAsPdf(page: 1);
      if (result == null) return; // user cancelled

      final sourcePath = result.pdfUri;
      if (sourcePath.isEmpty) {
        _snack('Scan did not produce a file. Please try again.');
        return;
      }

      const fileName = 'answer_key_scan.pdf';
      final savedPath = await _savePdfToDownloads(sourcePath, fileName);
      if (savedPath == null) {
        _snack('Could not save scanned PDF. Please try again.');
        return;
      }

      if (mounted) {
        setState(() {
          _answerKeyPath = savedPath;
          _answerKeyFileName = savedPath.split('/').last;
        });
        _snack('Answer key saved to Downloads ✓');
      }
    } on DocScanException catch (e) {
      debugPrint('Scan error: ${e.code} - ${e.message}');
      if (e.code == 'SCAN_FAILED') {
        await _showScannerUnavailableDialog(isAnswerKey: true);
      } else {
        _snack('Scan failed: ${e.message}');
      }
    } catch (e) {
      debugPrint('Unexpected scan error: $e');
      _snack('Scan failed. Please try again.');
    }
  }

  /// Scans one or more pages → PDF → saves to Downloads → appends to sheets list.
  Future<void> _scanAnswerSheets() async {
    try {
      final result =
      await FlutterDocScanner().getScannedDocumentAsPdf(page: 20);
      if (result == null) return; // user cancelled

      final sourcePath = result.pdfUri;
      if (sourcePath.isEmpty) {
        _snack('Scan did not produce a file. Please try again.');
        return;
      }

      final stamp = DateTime.now().millisecondsSinceEpoch;
      final fileName = 'answer_sheet_scan_$stamp.pdf';
      final savedPath = await _savePdfToDownloads(sourcePath, fileName);
      if (savedPath == null) {
        _snack('Could not save scanned PDF. Please try again.');
        return;
      }

      if (mounted) {
        setState(() {
          _answerSheetFiles = [
            ..._answerSheetFiles,
            PlatformFile(
              name: savedPath.split('/').last,
              path: savedPath,
              size: File(savedPath).lengthSync(),
            ),
          ];
        });
        _snack('Answer sheet saved to Downloads ✓');
      }
    } on DocScanException catch (e) {
      debugPrint('Scan error: ${e.code} - ${e.message}');
      if (e.code == 'SCAN_FAILED') {
        await _showScannerUnavailableDialog(isAnswerKey: false);
      } else {
        _snack('Scan failed: ${e.message}');
      }
    } catch (e) {
      debugPrint('Unexpected scan error: $e');
      _snack('Scan failed. Please try again.');
    }
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  API — single multipart streaming call
  // ══════════════════════════════════════════════════════════════════════════

  Future<bool> _fileUpload(BuildContext context) async {
    _cancelled = false;

    // ── SIMULATION BLOCK — comment out this entire block when real API is ready ──
    /*{
      const opts = ['A', 'B', 'C', 'D', 'E'];
      final sheetNames = _answerSheetFiles.isNotEmpty
          ? _answerSheetFiles.map((f) => f.name).toList()
          : ['Student_A.pdf', 'Student_B.pdf', 'Student_C.pdf'];
      final random = List.generate(sheetNames.length, (i) {
        const total = 25;
        final score = 8 + (i * 7 + 3) % 14;
        final blank = (i * 3 + 1) % 4;
        final wrongCount = (total - score - blank).clamp(0, total);

        final correctAnswers = List.generate(total, (q) => opts[(q * 3 + i) % 5]);

        final wrongSet = <int>{};
        for (int w = 0; wrongSet.length < wrongCount && w < total * 3; w++) {
          wrongSet.add((i * 5 + w * 7 + w) % total + 1);
        }

        final blankSet = <int>{};
        for (int b = total; blankSet.length < blank && b >= 1; b--) {
          if (!wrongSet.contains(b)) blankSet.add(b);
        }

        final answers = List.generate(total, (qi) {
          final q = qi + 1;
          final correct = correctAnswers[qi];
          final String marked;
          if (blankSet.contains(q)) {
            marked = '';
          } else if (wrongSet.contains(q)) {
            final ci = opts.indexOf(correct);
            marked = opts[(ci + 1 + qi % 3) % 5];
          } else {
            marked = correct;
          }
          return <String, dynamic>{'question': q, 'marked': marked, 'correct': correct};
        });

        return <String, dynamic>{
          'sheet_name': sheetNames[i],
          'score': score,
          'total': total,
          'percent': double.parse(((score / total) * 100).toStringAsFixed(1)),
          'blank': blank,
          'wrong_list': wrongSet.toList(),
          'answers': answers,
        };
      });

      // Simulate upload progress
      for (int p = 0; p <= 100; p += 5) {
        if (_cancelled) return false;
        await Future.delayed(const Duration(milliseconds: 80));
        if (mounted) setState(() => _uploadProgress = p / 100);
      }

      // Simulate server grading delay
      if (mounted) setState(() => _isGrading = true);
      await Future.delayed(const Duration(seconds: 1));

      // Stream results one by one with delay (simulates NDJSON streaming)
      for (final result in random) {
        if (_cancelled) return false;
        await Future.delayed(const Duration(milliseconds: 800));
        if (mounted) setState(() => _gradingResults.add(result));
      }

      return !_cancelled;
    }*/
    // ── END SIMULATION BLOCK ────────────────────────────────────────────────────

    // ignore: dead_code
    try {
      debugPrint(
          'fileUpload: key=$_answerKeyFileName  sheets=${_answerSheetFiles.length}');

      final request = http.MultipartRequest(
        'POST',
        Uri.parse('$_kBase/fileUpload'),
      );

      // answer_key field
      request.files.add(await http.MultipartFile.fromPath(
        'answer_key',
        _answerKeyPath!,
        contentType: http.MediaType('application', 'pdf'),
        filename: _answerKeyFileName,
      ));

      // answer_sheets field — one entry per file
      for (final file in _answerSheetFiles) {
        if (file.path == null) continue;
        final ext = file.name.split('.').last.toLowerCase();
        final mime = switch (ext) {
          'pdf' => http.MediaType('application', 'pdf'),
          'png' => http.MediaType('image', 'png'),
          _ => http.MediaType('image', 'jpeg'),
        };
        request.files.add(await http.MultipartFile.fromPath(
          'answer_sheets',
          file.path!,
          contentType: mime,
          filename: file.name,
        ));
      }

      debugPrint('fileUpload: sending ${request.files.length} file(s)…');

      // Finalize to get full body bytes (enables progress tracking)
      final bodyBytes = await request.finalize().toBytes();
      if (_cancelled) return false;
      final fullSize = bodyBytes.length;
      final contentType = request.headers['content-type'] ??
          request.headers['Content-Type'] ??
          'multipart/form-data';

      // Open raw HTTP request (10 s connection timeout so we fail fast when
      // the server is unreachable rather than hanging indefinitely)
      final ioRequest =
      await (HttpClient()
            ..findProxy = (uri) => "DIRECT"
            ..connectionTimeout = const Duration(seconds: 10))
          .postUrl(Uri.parse('$_kBase/fileUpload'));
      ioRequest.headers.set('Content-Type', contentType);
      ioRequest.headers.set('Content-Length', fullSize.toString());

      // Stream body in chunks with progress updates
      const chunkSize = 32 * 1024; // 32 KB
      int sentBytes = 0;
      for (int offset = 0; offset < bodyBytes.length; offset += chunkSize) {
        if (_cancelled) {
          ioRequest.abort();
          return false;
        }
        final end = (offset + chunkSize).clamp(0, bodyBytes.length);
        ioRequest.add(bodyBytes.sublist(offset, end));
        sentBytes = end;
        if (mounted) setState(() => _uploadProgress = sentBytes / fullSize);
      }
      if (_cancelled) {
        ioRequest.abort();
        return false;
      }

      final ioResponse = await ioRequest.close();
      debugPrint('fileUpload status: ${ioResponse.statusCode}');

      // Upload is complete — server is now grading
      if (mounted) setState(() => _isGrading = true);

      if (ioResponse.statusCode != 200) {
        final body = await ioResponse.transform(utf8.decoder).join();
        debugPrint('fileUpload error body: $body');
        showToast(
          'Server error ${ioResponse.statusCode}, please try again!',
          context,
          isShort: false,
        );
        return false;
      }

      // Stream NDJSON response — one JSON result object per line
      await for (final line in ioResponse
          .transform(utf8.decoder)
          .transform(const LineSplitter())) {
        if (_cancelled) break;
        if (line.trim().isEmpty) continue;
        try {
          final result = jsonDecode(line) as Map<String, dynamic>;
          if (mounted) setState(() => _gradingResults.add(result));
        } catch (e) {
          debugPrint('Failed to parse result line: $line — $e');
        }
      }

      return !_cancelled;
    } catch (e) {
      if (_cancelled) return false;
      debugPrint('Exception: $e');
      if (e is SocketException || e is TimeoutException) {
        showToast(
          'Cannot reach server. Make sure the server is running and the device is on the same network.',
          context,
          isShort: false,
        );
        return false;
      }
      showToast('Unable to process files, please try again!', context);
      return false;
    }
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  File Picking
  // ══════════════════════════════════════════════════════════════════════════

  Future<void> _pickAnswerKey() async {
    final picked = await FilePicker.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['pdf', 'txt'],
    );
    if (picked == null || picked.files.isEmpty) return;
    setState(() {
      _answerKeyFileName = picked.files.first.name;
      _answerKeyPath = picked.files.first.path;
    });
  }

  Future<void> _pickAnswerSheets() async {
    final picked = await FilePicker.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['pdf', 'jpg', 'jpeg', 'png'],
      allowMultiple: true,
    );
    if (picked == null || picked.files.isEmpty) return;
    setState(() => _answerSheetFiles = picked.files);
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  Grading orchestration
  // ══════════════════════════════════════════════════════════════════════════

  Future<void> _startGrading() async {
    // Skip file validation when simulation block is active
    const _simActive = false;
    if (!_simActive) {
      if (_answerKeyPath == null) {
        _snack('Please upload an answer key first.');
        return;
      }
      if (_answerSheetFiles.isEmpty) {
        _snack('Please upload answer sheets to grade.');
        return;
      }
      if (!await _checkNet()) return;
    }

    setState(() {
      _busy = true;
      _isGrading = false;
      _errorMessage = null;
      _gradingResults.clear();
      _hasResults = false;
    });

    final success = await _fileUpload(context);

    if (!mounted) return;
    if (_cancelled) {
      setState(() => _cancelled = false);
      return;
    }
    setState(() {
      _busy = false;
      _isGrading = false;
      _hasResults = success && _gradingResults.isNotEmpty;
      _errorMessage =
      success ? null : '❌ Upload failed. Please try again.';
    });
  }

  // ── CSV Export ──────────────────────────────────────────────────────────────
  Future<void> _exportCsv() async {
    if (_gradingResults.isEmpty) {
      _snack('No results to export.');
      return;
    }

    try {
      final List<List<dynamic>> rows = [
        ['Sheet Name', 'Score', 'Total', 'Percent', 'Blank', 'Wrong Count'],
      ];
      for (final r in _gradingResults) {
        rows.add([
          r['sheet_name'] ?? 'Unknown',
          r['score'] ?? 0,
          r['total'] ?? 0,
          r['percent'] ?? 0.0,
          r['blank'] ?? 0,
          (r['wrong_list'] as List?)?.length ?? 0,
        ]);
      }

      final buf = StringBuffer();
      for (final row in rows) {
        buf.writeln(row.map((cell) {
          final s = cell.toString();
          return s.contains(',') || s.contains('"')
              ? '"${s.replaceAll('"', '""')}"'
              : s;
        }).join(','));
      }

      Directory downloadsDir;
      if (Platform.isAndroid) {
        downloadsDir = Directory('/storage/emulated/0/Download');
        if (!await downloadsDir.exists()) {
          final ext = await getExternalStorageDirectory();
          downloadsDir = ext ?? await getApplicationDocumentsDirectory();
        }
      } else {
        downloadsDir = await getApplicationDocumentsDirectory();
      }

      String fileName = 'results.csv';
      File file = File('${downloadsDir.path}/$fileName');
      if (await file.exists()) {
        final stamp = DateTime.now().millisecondsSinceEpoch;
        fileName = 'results_$stamp.csv';
        file = File('${downloadsDir.path}/$fileName');
      }

      await file.writeAsString(buf.toString());
      if (mounted) _snack('File saved to downloads folder');
    } catch (e) {
      debugPrint('CSV export error: $e');
      _snack('Failed to export CSV. Please try again.');
    }
  }

  // ── Detailed CSV Export (per-question) ──────────────────────────────────────
  Future<void> _exportDetailedCsv() async {
    if (_gradingResults.isEmpty) {
      _snack('No results to export.');
      return;
    }

    try {
      final List<List<dynamic>> rows = [
        ['sheet', 'key', 'question', 'marked', 'correct', 'status', 'is_correct'],
      ];

      for (final r in _gradingResults) {
        final sheetName = r['sheet_name'] as String? ?? 'Unknown';
        final answers = r['answers'] as List?;
        final wrongSet = (r['wrong_list'] as List?)?.map((e) => e as int).toSet() ?? <int>{};
        final total = r['total'] as int? ?? 0;

        if (answers != null && answers.isNotEmpty) {
          for (final a in answers) {
            final aMap = a as Map<String, dynamic>;
            final question = aMap['question'] as int? ?? 0;
            final marked = (aMap['marked'] as String?) ?? '';
            final correct = (aMap['correct'] as String?) ?? '';
            String status;
            int isCorrect;
            if (marked.isEmpty) {
              status = 'blank';
              isCorrect = 0;
            } else if (marked == correct) {
              status = 'correct';
              isCorrect = 1;
            } else {
              status = 'wrong';
              isCorrect = 0;
            }
            rows.add([sheetName, 1, question, marked, correct, status, isCorrect]);
          }
        } else {
          for (int q = 1; q <= total; q++) {
            final isWrong = wrongSet.contains(q);
            rows.add([sheetName, 1, q, '', '', isWrong ? 'wrong' : 'correct', isWrong ? 0 : 1]);
          }
        }
      }

      final buf = StringBuffer();
      for (final row in rows) {
        buf.writeln(row.map((cell) {
          final s = cell.toString();
          return s.contains(',') || s.contains('"')
              ? '"${s.replaceAll('"', '""')}"'
              : s;
        }).join(','));
      }

      Directory downloadsDir;
      if (Platform.isAndroid) {
        downloadsDir = Directory('/storage/emulated/0/Download');
        if (!await downloadsDir.exists()) {
          final ext = await getExternalStorageDirectory();
          downloadsDir = ext ?? await getApplicationDocumentsDirectory();
        }
      } else {
        downloadsDir = await getApplicationDocumentsDirectory();
      }

      String fileName = 'results_detailed.csv';
      File file = File('${downloadsDir.path}/$fileName');
      if (await file.exists()) {
        final stamp = DateTime.now().millisecondsSinceEpoch;
        fileName = 'results_detailed_$stamp.csv';
        file = File('${downloadsDir.path}/$fileName');
      }

      await file.writeAsString(buf.toString());
      if (mounted) _snack('Detailed CSV saved to downloads folder');
    } catch (e) {
      debugPrint('Detailed CSV export error: $e');
      _snack('Failed to export detailed CSV. Please try again.');
    }
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  Build
  // ══════════════════════════════════════════════════════════════════════════

  @override
  Widget build(BuildContext context) {
    final appState = context.watch<AppStateProvider>();
    final media = MediaQuery.of(context).size;
    final isLandscape =
        MediaQuery.of(context).orientation == Orientation.landscape;

    return MediaQuery(
      data: MediaQuery.of(context)
          .copyWith(textScaler: const TextScaler.linear(1)),
      child: Scaffold(
        key: _scaffoldKey,
        backgroundColor: surfaceBg,
       /* drawer: CustomDrawer(
          onNavigateToGrader: () {},
          onNavigateToAIChat: () => appState.setPage(1),
          currentPageIndex: 0,
        ),*/
        appBar: _buildAppBar(media, isLandscape),
        body: OrientationBuilder(
          builder: (context, orientation) =>
              _buildBody(context, orientation == Orientation.landscape),
        ),
      ),
    );
  }

  // ── AppBar ─────────────────────────────────────────────────────────────────
  PreferredSizeWidget _buildAppBar(Size media, bool isLandscape) => AppBar(
    backgroundColor: tealPrimary,
    elevation: 0,
    toolbarHeight: media.shortestSide * 0.15,
    /*leading: Builder(
      builder: (ctx) => IconButton(
        icon: Icon(Icons.menu_rounded,
            color: colorWhite,
            size: isLandscape
                ? media.height * 0.07
                : media.height * 0.05),
        onPressed: () => Scaffold.of(ctx).openDrawer(),
      ),
    ),*/
    title: customText(
      text: 'OMR Grader',
      context: context,
      color: colorWhite,
      fontSize: media.shortestSide * 0.055,
      isBold: true,
    ),
    centerTitle: true,
  );

  // ── Body ───────────────────────────────────────────────────────────────────
  Widget _buildBody(BuildContext context, bool isLandscape) {
    final media = MediaQuery.of(context).size;

    return SingleChildScrollView(
      padding: EdgeInsets.symmetric(
        horizontal: isLandscape ? media.width * 0.06 : media.width * 0.05,
        vertical: media.height * 0.03,
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // ── Upload cards ──────────────────────────────────────────────────
          if (isLandscape)
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Expanded(child: _buildUploadKeyCard(media)),
                SizedBox(width: media.width * 0.03),
                Expanded(child: _buildUploadSheetsCard(media)),
              ],
            )
          else ...[
            _buildUploadKeyCard(media),
            SizedBox(height: media.height * 0.02),
            _buildUploadSheetsCard(media),
          ],

          SizedBox(height: media.height * 0.03),

          // ── Start Grading button ──────────────────────────────────────────
          _buildGradingButton(media, isLandscape),
          if (_busy) ...[
            SizedBox(height: media.height * 0.01),
            _buildCancelButton(media, isLandscape),
          ],
          SizedBox(height: media.height * 0.025),

          // ── Upload progress (visible while uploading) ─────────────────────
          if (_busy && !_isGrading) _buildUploadProgress(media),

          // ── "Grading files, please wait…" ─────────────────────────────────
          if (_isGrading) _buildGradingMessage(media),

          // ── Error message ─────────────────────────────────────────────────
          if (_errorMessage != null) ...[
            SizedBox(height: media.height * 0.01),
            customText(
              text: _errorMessage!,
              context: context,
              fontSize: media.shortestSide * 0.036,
              color: colorRed,
              isBold: true,
            ),
          ],

          // ── Download + result cards ────────────────────────────────────────
          if (_hasResults && !_busy) ...[
            SizedBox(height: media.height * 0.015),
            _buildDownloadButton(media),
            SizedBox(height: media.height * 0.01),
            _buildDetailedDownloadButton(media),
            SizedBox(height: media.height * 0.02),
            if (isLandscape)
              Wrap(
                spacing: media.width * 0.02,
                runSpacing: 0,
                children: _gradingResults
                    .map((r) => SizedBox(
                  width: (media.width * 0.88 - media.width * 0.02) / 2,
                  child: _buildResultCard(r, media),
                ))
                    .toList(),
              )
            else
              ..._gradingResults.map((r) => _buildResultCard(r, media)),
          ],
        ],
      ),
    );
  }

  // ── Upload Answer Key Card ──────────────────────────────────────────────────
  Widget _buildUploadKeyCard(Size media) {
    final iconBoxSize = media.shortestSide * 0.14;
    return GestureDetector(
      // ── NOW opens the source picker bottom sheet ──────────────────────────
      onTap: _busy ? null : () => _showUploadSourceSheet(isAnswerKey: true),
      child: Container(
        padding: EdgeInsets.all(media.shortestSide * 0.04),
        decoration: BoxDecoration(
          color: cardBg,
          borderRadius: BorderRadius.circular(20),
          boxShadow: [
            BoxShadow(
              color: colorBlack.withValues(alpha: 0.05),
              blurRadius: 10,
              offset: const Offset(0, 4),
            ),
          ],
        ),
        child: Row(
          children: [
            Container(
              width: iconBoxSize,
              height: iconBoxSize,
              decoration: BoxDecoration(
                  color: tealPrimary,
                  borderRadius: BorderRadius.circular(16)),
              child: Icon(Icons.cloud_upload_rounded,
                  color: colorWhite, size: iconBoxSize * 0.55),
            ),
            SizedBox(width: media.shortestSide * 0.04),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  customText(
                    text: '1. Upload Answer Key',
                    context: context,
                    fontSize: media.shortestSide * 0.042,
                    isBold: true,
                    color: colorBlack87,
                    textAlign: TextAlign.start,
                  ),
                  const SizedBox(height: 4),
                  customText(
                    text: _answerKeyFileName ?? 'Tap to select',
                    context: context,
                    fontSize: media.shortestSide * 0.033,
                    color: _answerKeyFileName != null
                        ? tealPrimary
                        : colorBlack54,
                    isBold: _answerKeyFileName != null,
                    maxLines: 1,
                    textAlign: TextAlign.start,
                  ),
                ],
              ),
            ),
            if (_answerKeyFileName != null)
              GestureDetector(
                onTap: _busy
                    ? null
                    : () => setState(() {
                  _answerKeyFileName = null;
                  _answerKeyPath = null;
                }),
                child: Icon(
                  Icons.close_rounded,
                  color: _busy ? colorBlack54 : colorRed,
                  size: media.shortestSide * 0.055,
                ),
              ),
          ],
        ),
      ),
    );
  }

  // ── Upload Answer Sheets Card ───────────────────────────────────────────────
  Widget _buildUploadSheetsCard(Size media) {
    final fileCount = _answerSheetFiles.length;
    final iconBoxSize = media.shortestSide * 0.14;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        GestureDetector(
          // ── NOW opens the source picker bottom sheet ────────────────────
          onTap: _busy ? null : () => _showUploadSourceSheet(isAnswerKey: false),
          child: Container(
            padding: EdgeInsets.all(media.shortestSide * 0.04),
            decoration: BoxDecoration(
              color: cardBg,
              borderRadius: BorderRadius.circular(20),
              boxShadow: [
                BoxShadow(
                  color: colorBlack.withValues(alpha: 0.05),
                  blurRadius: 10,
                  offset: const Offset(0, 4),
                ),
              ],
            ),
            child: Row(
              children: [
                Container(
                  width: iconBoxSize,
                  height: iconBoxSize,
                  decoration: BoxDecoration(
                      color: tealPrimary,
                      borderRadius: BorderRadius.circular(16)),
                  child: Icon(Icons.cloud_upload_rounded,
                      color: colorWhite, size: iconBoxSize * 0.55),
                ),
                SizedBox(width: media.shortestSide * 0.04),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      customText(
                        text: '2. Upload Answer Sheets',
                        context: context,
                        fontSize: media.shortestSide * 0.042,
                        isBold: true,
                        color: colorBlack87,
                        textAlign: TextAlign.start,
                      ),
                      const SizedBox(height: 4),
                      customText(
                        text: fileCount > 0
                            ? '$fileCount file(s) selected — tap to add more'
                            : 'Tap to select',
                        context: context,
                        fontSize: media.shortestSide * 0.033,
                        color: fileCount > 0 ? tealPrimary : colorBlack54,
                        isBold: fileCount > 0,
                        textAlign: TextAlign.start,
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
        if (fileCount > 0 && !_busy) ...[
          SizedBox(height: media.shortestSide * 0.02),
          Wrap(
            spacing: 6,
            runSpacing: 6,
            children: List.generate(
              fileCount,
                  (i) => _buildFileChip(_answerSheetFiles[i].name, i, media),
            ),
          ),
        ],
      ],
    );
  }

  Widget _buildFileChip(String name, int index, Size media) {
    return Chip(
      backgroundColor: tealLight,
      side: BorderSide(color: tealPrimary.withValues(alpha: 0.3)),
      labelPadding: const EdgeInsets.symmetric(horizontal: 4),
      label: Text(
        name,
        overflow: TextOverflow.ellipsis,
        style: TextStyle(
          fontSize: media.shortestSide * 0.03,
          color: tealDark,
          fontWeight: FontWeight.w500,
        ),
      ),
      deleteIcon: Icon(
        Icons.close,
        size: media.shortestSide * 0.04,
        color: tealDark,
      ),
      onDeleted: () => setState(() => _answerSheetFiles.removeAt(index)),
    );
  }

  // ── Start Grading Button ────────────────────────────────────────────────────
  Widget _buildGradingButton(Size media, bool isLandscape) {
    return Center(
      child: SizedBox(
        width: isLandscape ? media.width * 0.45 : media.width * 0.6,
        height: media.shortestSide * 0.13,
        child: ElevatedButton(
          onPressed: _busy ? null : _startGrading,
          style: ElevatedButton.styleFrom(
            backgroundColor: tealPrimary,
            foregroundColor: colorWhite,
            disabledBackgroundColor: tealLight,
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(14)),
            elevation: 4,
          ),
          child: _busy
              ? Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const SizedBox(
                width: 20,
                height: 20,
                child: CircularProgressIndicator(
                    color: Colors.white, strokeWidth: 2),
              ),
              const SizedBox(width: 12),
              customText(
                text: _isGrading ? 'Grading…' : 'Uploading…',
                context: context,
                fontSize: media.shortestSide * 0.04,
                isBold: true,
                color: colorWhite,
              ),
            ],
          )
              : customText(
            text: 'Start Grading',
            context: context,
            fontSize: media.shortestSide * 0.046,
            isBold: true,
            color: colorWhite,
          ),
        ),
      ),
    );
  }

  // ── Cancel Upload Button ────────────────────────────────────────────────────
  Widget _buildCancelButton(Size media, bool isLandscape) {
    return Center(
      child: SizedBox(
        width: isLandscape ? media.width * 0.45 : media.width * 0.6,
        child: TextButton.icon(
          onPressed: _cancelUpload,
          icon: Icon(Icons.cancel_outlined,
              color: colorRed, size: media.shortestSide * 0.05),
          label: customText(
            text: 'Cancel',
            context: context,
            fontSize: media.shortestSide * 0.038,
            isBold: true,
            color: colorRed,
          ),
          style: TextButton.styleFrom(foregroundColor: colorRed),
        ),
      ),
    );
  }

  // ── Upload Progress Section ─────────────────────────────────────────────────
  Widget _buildUploadProgress(Size media) {
    final percent = (_uploadProgress * 100).toInt();
    return Container(
      padding: EdgeInsets.all(media.shortestSide * 0.04),
      decoration: BoxDecoration(
        color: cardBg,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Row(
                children: [
                  SizedBox(
                    width: media.shortestSide * 0.055,
                    height: media.shortestSide * 0.055,
                    child: CircularProgressIndicator(
                        color: tealPrimary, strokeWidth: 2.5),
                  ),
                  SizedBox(width: media.shortestSide * 0.04),
                  customText(
                    text: 'Uploading files…',
                    context: context,
                    fontSize: media.shortestSide * 0.038,
                    isBold: true,
                    color: tealDark,
                    textAlign: TextAlign.start,
                  ),
                ],
              ),
              customText(
                text: '$percent%',
                context: context,
                fontSize: media.shortestSide * 0.042,
                isBold: true,
                color: tealPrimary,
              ),
            ],
          ),
          SizedBox(height: media.shortestSide * 0.025),
          ClipRRect(
            borderRadius: BorderRadius.circular(8),
            child: LinearProgressIndicator(
              value: _uploadProgress,
              minHeight: media.shortestSide * 0.015,
              backgroundColor: tealLight,
              color: tealPrimary,
            ),
          ),
        ],
      ),
    );
  }

  // ── "Grading files, please wait…" section ──────────────────────────────────
  Widget _buildGradingMessage(Size media) {
    return Container(
      padding: EdgeInsets.all(media.shortestSide * 0.04),
      decoration: BoxDecoration(
        color: tealLight,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              SizedBox(
                width: media.shortestSide * 0.055,
                height: media.shortestSide * 0.055,
                child: CircularProgressIndicator(
                  color: tealPrimary,
                  strokeWidth: 2.5,
                ),
              ),
              SizedBox(width: media.shortestSide * 0.04),
              Expanded(
                child: customText(
                  text: 'Calculating result, please wait ...',
                  context: context,
                  fontSize: media.shortestSide * 0.04,
                  isBold: true,
                  color: tealDark,
                  textAlign: TextAlign.start,
                ),
              ),
            ],
          ),
          SizedBox(height: media.shortestSide * 0.025),
          LinearProgressIndicator(
            minHeight: 3,
            backgroundColor: tealPrimary.withValues(alpha: 0.15),
            color: tealPrimary,
          ),
        ],
      ),
    );
  }

  // ── Download Results Button ─────────────────────────────────────────────────
  Widget _buildDownloadButton(Size media) {
    return ElevatedButton.icon(
      onPressed: _exportCsv,
      icon: const Icon(Icons.download_rounded),
      label: customText(
        text: 'Download Results',
        context: context,
        fontSize: media.shortestSide * 0.042,
        isBold: true,
        color: colorWhite,
      ),
      style: ElevatedButton.styleFrom(
        backgroundColor: tealDark,
        foregroundColor: colorWhite,
        minimumSize: Size(double.infinity, media.shortestSide * 0.13),
        shape:
        RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
        elevation: 3,
      ),
    );
  }

  // ── Download Detailed CSV Button ────────────────────────────────────────────
  Widget _buildDetailedDownloadButton(Size media) {
    return ElevatedButton.icon(
      onPressed: _exportDetailedCsv,
      icon: const Icon(Icons.table_chart_rounded),
      label: customText(
        text: 'Download Detailed CSV',
        context: context,
        fontSize: media.shortestSide * 0.042,
        isBold: true,
        color: colorWhite,
      ),
      style: ElevatedButton.styleFrom(
        backgroundColor: tealAccent,
        foregroundColor: colorWhite,
        minimumSize: Size(double.infinity, media.shortestSide * 0.13),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
        elevation: 3,
      ),
    );
  }

  // ── Result Card ─────────────────────────────────────────────────────────────
  Widget _buildResultCard(Map<String, dynamic> r, Size media) {
    return _ResultCard(
      result: r,
      media: media,
      onChanged: () => setState(() {}),
    );
  }
}

// ── Result Card (expandable with per-question editing) ────────────────────────
class _ResultCard extends StatefulWidget {
  final Map<String, dynamic> result;
  final Size media;
  final VoidCallback onChanged;

  const _ResultCard({
    required this.result,
    required this.media,
    required this.onChanged,
  });

  @override
  State<_ResultCard> createState() => _ResultCardState();
}

class _ResultCardState extends State<_ResultCard> {
  bool _expanded = false;

  Map<String, dynamic> get r => widget.result;
  Size get media => widget.media;

  void _recalculate() {
    final answers = r['answers'] as List?;
    if (answers == null) return;
    int score = 0, blank = 0;
    final wrongList = <int>[];
    for (final a in answers) {
      final aMap = a as Map<String, dynamic>;
      final q = aMap['question'] as int? ?? 0;
      final marked = (aMap['marked'] as String? ?? '').trim();
      final correct = (aMap['correct'] as String? ?? '').trim();
      if (marked.isEmpty) {
        blank++;
      } else if (marked == correct) {
        score++;
      } else {
        wrongList.add(q);
      }
    }
    final total = answers.length;
    r['score'] = score;
    r['blank'] = blank;
    r['wrong_list'] = wrongList;
    r['total'] = total;
    r['percent'] = total > 0
        ? double.parse(((score / total) * 100).toStringAsFixed(1))
        : 0.0;
  }

  void _editAnswer(int index, String newMarked) {
    final answers = r['answers'] as List?;
    if (answers == null) return;
    setState(() {
      (answers[index] as Map<String, dynamic>)['marked'] = newMarked;
      _recalculate();
    });
    widget.onChanged();
  }

  void _showAnswerPicker(BuildContext context, int index, String currentMarked) {
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
      ),
      backgroundColor: colorWhite,
      builder: (_) {
        return SafeArea(
          child: Padding(
            padding: EdgeInsets.symmetric(
              horizontal: media.width * 0.06,
              vertical: media.height * 0.025,
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Center(
                  child: Container(
                    width: 40,
                    height: 4,
                    margin: const EdgeInsets.only(bottom: 16),
                    decoration: BoxDecoration(
                      color: colorBlack38,
                      borderRadius: BorderRadius.circular(2),
                    ),
                  ),
                ),
                customText(
                  text: 'Q${index + 1}: Select Marked Answer',
                  context: context,
                  fontSize: media.shortestSide * 0.042,
                  isBold: true,
                  color: colorBlack87,
                  textAlign: TextAlign.start,
                ),
                SizedBox(height: media.shortestSide * 0.04),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    ...['A', 'B', 'C', 'D', 'E'].map((opt) {
                      final isSelected = opt == currentMarked;
                      return GestureDetector(
                        onTap: () {
                          Navigator.pop(context);
                          _editAnswer(index, opt);
                        },
                        child: Container(
                          width: media.shortestSide * 0.13,
                          height: media.shortestSide * 0.13,
                          decoration: BoxDecoration(
                            color: isSelected ? tealPrimary : colorWhite,
                            borderRadius: BorderRadius.circular(10),
                            border: Border.all(
                              color: isSelected ? tealPrimary : colorBlack38,
                              width: 1.5,
                            ),
                          ),
                          alignment: Alignment.center,
                          child: customText(
                            text: opt,
                            context: context,
                            fontSize: media.shortestSide * 0.048,
                            isBold: true,
                            color: isSelected ? colorWhite : colorBlack87,
                          ),
                        ),
                      );
                    }),
                    GestureDetector(
                      onTap: () {
                        Navigator.pop(context);
                        _editAnswer(index, '');
                      },
                      child: Container(
                        width: media.shortestSide * 0.17,
                        height: media.shortestSide * 0.13,
                        decoration: BoxDecoration(
                          color: currentMarked.isEmpty ? colorOrange : colorWhite,
                          borderRadius: BorderRadius.circular(10),
                          border: Border.all(
                            color: currentMarked.isEmpty ? colorOrange : colorBlack38,
                            width: 1.5,
                          ),
                        ),
                        alignment: Alignment.center,
                        child: customText(
                          text: 'Blank',
                          context: context,
                          fontSize: media.shortestSide * 0.035,
                          isBold: true,
                          color: currentMarked.isEmpty ? colorWhite : colorBlack54,
                        ),
                      ),
                    ),
                  ],
                ),
                SizedBox(height: media.shortestSide * 0.02),
              ],
            ),
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    final score = r['score'] as int? ?? 0;
    final total = r['total'] as int? ?? 0;
    final percent = r['percent'] as double? ?? 0.0;
    final blank = r['blank'] as int? ?? 0;
    final sheetName = r['sheet_name'] as String? ?? 'Unknown';
    final wrong = r['wrong_list'] as List? ?? [];
    final answers = r['answers'] as List?;
    final hasDetail = answers != null && answers.isNotEmpty;

    return Container(
      margin: EdgeInsets.only(bottom: media.height * 0.015),
      decoration: BoxDecoration(
        color: colorWhite,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: colorBlack.withValues(alpha: 0.05),
            blurRadius: 8,
            offset: const Offset(0, 3),
          ),
        ],
      ),
      child: Column(
        children: [
          // ── Summary header ────────────────────────────────────────────────
          InkWell(
            onTap: hasDetail ? () => setState(() => _expanded = !_expanded) : null,
            borderRadius: BorderRadius.circular(16),
            child: Padding(
              padding: EdgeInsets.all(media.shortestSide * 0.04),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Expanded(
                        child: customText(
                          text: sheetName,
                          context: context,
                          fontSize: media.shortestSide * 0.04,
                          isBold: true,
                          color: colorBlack87,
                          textAlign: TextAlign.start,
                        ),
                      ),
                      if (hasDetail)
                        Icon(
                          _expanded ? Icons.expand_less_rounded : Icons.expand_more_rounded,
                          color: colorBlack54,
                          size: media.shortestSide * 0.055,
                        ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  ClipRRect(
                    borderRadius: BorderRadius.circular(8),
                    child: LinearProgressIndicator(
                      value: total > 0 ? score / total : 0,
                      minHeight: 8,
                      backgroundColor: tealLight,
                      color: percent >= 70
                          ? colorGreen
                          : percent >= 40
                          ? colorOrange
                          : colorRed,
                    ),
                  ),
                  const SizedBox(height: 10),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      _StatChip(label: 'Score', value: '$score/$total', color: tealPrimary),
                      _StatChip(label: 'Percent', value: '${percent.toStringAsFixed(1)}%', color: tealAccent),
                      _StatChip(label: 'Blank', value: '$blank', color: colorOrange),
                      _StatChip(label: 'Wrong', value: '${wrong.length}', color: colorRed),
                    ],
                  ),
                ],
              ),
            ),
          ),

          // ── Per-question detail ───────────────────────────────────────────
          if (_expanded && hasDetail) ...[
            Divider(height: 1, color: colorBlack12),
            Padding(
              padding: EdgeInsets.all(media.shortestSide * 0.03),
              child: Column(
                children: [
                  _buildQuestionTableHeader(),
                  const SizedBox(height: 4),
                  ...List.generate(answers.length, (i) {
                    return _buildQuestionRow(context, i, answers[i] as Map<String, dynamic>);
                  }),
                ],
              ),
            ),
          ],
        ],
      ),
    );
  }

  Widget _buildQuestionTableHeader() {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        children: [
          SizedBox(
            width: media.shortestSide * 0.1,
            child: customText(
              text: 'Q#',
              context: context,
              fontSize: media.shortestSide * 0.03,
              isBold: true,
              color: colorBlack54,
            ),
          ),
          Expanded(
            child: customText(
              text: 'Marked',
              context: context,
              fontSize: media.shortestSide * 0.03,
              isBold: true,
              color: colorBlack54,
              textAlign: TextAlign.center,
            ),
          ),
          Expanded(
            child: customText(
              text: 'Correct',
              context: context,
              fontSize: media.shortestSide * 0.03,
              isBold: true,
              color: colorBlack54,
              textAlign: TextAlign.center,
            ),
          ),
          SizedBox(
            width: media.shortestSide * 0.24,
            child: customText(
              text: 'Status',
              context: context,
              fontSize: media.shortestSide * 0.03,
              isBold: true,
              color: colorBlack54,
              textAlign: TextAlign.center,
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildQuestionRow(BuildContext context, int index, Map<String, dynamic> a) {
    final question = a['question'] as int? ?? (index + 1);
    final marked = (a['marked'] as String?) ?? '';
    final correct = (a['correct'] as String?) ?? '';

    Color statusColor;
    String statusLabel;
    if (marked.isEmpty) {
      statusLabel = 'blank';
      statusColor = colorOrange;
    } else if (marked == correct) {
      statusLabel = 'correct';
      statusColor = colorGreen;
    } else {
      statusLabel = 'wrong';
      statusColor = colorRed;
    }

    return Padding(
      padding: EdgeInsets.symmetric(vertical: media.shortestSide * 0.008),
      child: Row(
        children: [
          SizedBox(
            width: media.shortestSide * 0.1,
            child: customText(
              text: '$question',
              context: context,
              fontSize: media.shortestSide * 0.033,
              color: colorBlack87,
            ),
          ),
          Expanded(
            child: Center(
              child: GestureDetector(
                onTap: () => _showAnswerPicker(context, index, marked),
                child: Container(
                  padding: EdgeInsets.symmetric(
                    horizontal: media.shortestSide * 0.02,
                    vertical: media.shortestSide * 0.01,
                  ),
                  decoration: BoxDecoration(
                    color: statusColor.withValues(alpha: 0.1),
                    borderRadius: BorderRadius.circular(6),
                    border: Border.all(color: statusColor.withValues(alpha: 0.35)),
                  ),
                  child: customText(
                    text: marked.isEmpty ? '—' : marked,
                    context: context,
                    fontSize: media.shortestSide * 0.035,
                    isBold: true,
                    color: statusColor,
                    textAlign: TextAlign.center,
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            child: Center(
              child: customText(
                text: correct.isEmpty ? '—' : correct,
                context: context,
                fontSize: media.shortestSide * 0.035,
                isBold: true,
                color: tealDark,
                textAlign: TextAlign.center,
              ),
            ),
          ),
          SizedBox(
            width: media.shortestSide * 0.24,
            child: Center(
              child: Container(
                padding: EdgeInsets.symmetric(
                  horizontal: media.shortestSide * 0.015,
                  vertical: media.shortestSide * 0.008,
                ),
                decoration: BoxDecoration(
                  color: statusColor.withValues(alpha: 0.12),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: customText(
                  text: statusLabel,
                  context: context,
                  fontSize: media.shortestSide * 0.028,
                  isBold: true,
                  color: statusColor,
                  textAlign: TextAlign.center,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// ── Stat Chip ──────────────────────────────────────────────────────────────────
class _StatChip extends StatelessWidget {
  final String label, value;
  final Color color;
  const _StatChip(
      {required this.label, required this.value, required this.color});

  @override
  Widget build(BuildContext context) {
    final media = MediaQuery.of(context).size;
    return Column(
      children: [
        customText(
          text: value,
          context: context,
          fontSize: media.shortestSide * 0.038,
          isBold: true,
          color: color,
        ),
        customText(
          text: label,
          context: context,
          fontSize: media.shortestSide * 0.03,
          color: colorBlack54,
        ),
      ],
    );
  }
}

// ── Source Option Tile ─────────────────────────────────────────────────────────
class _SourceOptionTile extends StatelessWidget {
  final IconData icon;
  final String label;
  final Color color;
  final Size media;
  final VoidCallback onTap;

  const _SourceOptionTile({
    required this.icon,
    required this.label,
    required this.color,
    required this.media,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: EdgeInsets.symmetric(
          horizontal: media.width * 0.05,
          vertical: media.shortestSide * 0.04,
        ),
        decoration: BoxDecoration(
          color: color.withValues(alpha: 0.08),
          borderRadius: BorderRadius.circular(16),
          border: Border.all(color: color.withValues(alpha: 0.25)),
        ),
        child: Row(
          children: [
            Container(
              padding: EdgeInsets.all(media.shortestSide * 0.028),
              decoration: BoxDecoration(
                color: color.withValues(alpha: 0.15),
                borderRadius: BorderRadius.circular(12),
              ),
              child: Icon(icon,
                  color: color, size: media.shortestSide * 0.06),
            ),
            SizedBox(width: media.width * 0.04),
            customText(
              text: label,
              context: context,
              fontSize: media.shortestSide * 0.04,
              isBold: true,
              color: colorBlack87,
              textAlign: TextAlign.start,
            ),
            const Spacer(),
            Icon(Icons.chevron_right_rounded,
                color: colorBlack54, size: media.shortestSide * 0.05),
          ],
        ),
      ),
    );
  }
}