// ignore_for_file: use_build_context_synchronously, deprecated_member_use

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

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../../app/injection_container.dart';
import '../../core/ai/detection_export.dart';
import '../../core/constants/app_constants.dart';
import '../../core/network/api_client.dart';
import '../../core/services/haptic_service.dart';
import '../../core/services/location_reporter_service.dart';
import '../../core/services/tts_service.dart';

// ---------------------------------------------------------------------------
// WalkGuideScreen
// ---------------------------------------------------------------------------

class WalkGuideScreen extends StatefulWidget {
  const WalkGuideScreen({super.key});

  @override
  State<WalkGuideScreen> createState() => _WalkGuideScreenState();
}

class _WalkGuideScreenState extends State<WalkGuideScreen> {
  bool _active = false;
  String _status = 'Ready';
  CameraController? _camera;
  DetectionResult? _lastDetection;

  @override
  void dispose() {
    _camera?.dispose();
    sl<LocationReporterService>().stop();
    super.dispose();
  }

  Future<void> _toggle() async {
    final next = !_active;
    if (next) {
      await _startCamera();
      await sl<LocationReporterService>().start(walkGuideActive: true);
    } else {
      await _camera?.dispose();
      _camera = null;
      await sl<LocationReporterService>().stop();
    }
    setState(() {
      _active = next;
      _status = next ? 'Camera stream active. YOLO ready.' : 'Stopped';
    });
    await sl<ApiClient>()
        .dio
        .post(next ? '/user/walkguide/start' : '/user/walkguide/stop');
    sl<TtsService>().speak(next ? 'WalkGuide dimulai' : 'WalkGuide dihentikan');
  }

  Future<void> _startCamera() async {
    if (_camera != null) return;
    try {
      final cameras = await availableCameras();
      if (cameras.isEmpty) return;
      final controller = CameraController(
          cameras.first, ResolutionPreset.medium,
          enableAudio: false);
      await controller.initialize();
      if (!mounted) {
        await controller.dispose();
        return;
      }
      setState(() => _camera = controller);
    } catch (_) {
      setState(() => _status = 'Camera unavailable. Demo mode active.');
    }
  }

  Future<void> _simulateObstacle() async {
    final detection = await sl<YoloDetector>().detectFallback();
    if (detection == null) return;
    _lastDetection = detection;
    await sl<ApiClient>().dio.post('/user/obstacle', data: {
      'label': detection.label,
      'confidence': detection.confidence,
      'direction': detection.directionName,
      'estimatedDist': detection.estimatedDistance,
      'lat': null,
      'lng': null,
    });
    await sl<HapticService>().obstacleClose();
    await sl<TtsService>().speakImmediate(detection.spokenId);
    setState(() => _status =
        'Obstacle: ${detection.label} ${detection.directionName} ${detection.estimatedDistance}');
  }

  @override
  Widget build(BuildContext context) {
    return _Page(
      title: 'WalkGuide',
      subtitle: 'On-device AI detection surface',
      actions: [
        IconButton(
            onPressed: () => context.go('/user/benchmark'),
            icon: const Icon(Icons.speed)),
        IconButton(
            onPressed: () => context.go('/user/pairing'),
            icon: const Icon(Icons.link)),
      ],
      child: Column(
        children: [
          Expanded(
            child: Container(
              width: double.infinity,
              decoration: BoxDecoration(
                  color: const Color(0xFF0F172A),
                  borderRadius: BorderRadius.circular(16)),
              child: Stack(
                children: [
                  if (_camera != null && _camera!.value.isInitialized)
                    Positioned.fill(child: CameraPreview(_camera!))
                  else
                    const Center(
                        child: Icon(Icons.videocam_outlined,
                            color: Colors.white30, size: 96)),
                  Positioned(
                      top: 16,
                      left: 16,
                      child: _Pill(
                          text: _active ? 'AI ACTIVE' : 'STANDBY',
                          color: _active ? Colors.green : Colors.orange)),
                  if (_lastDetection != null)
                    Positioned(
                      top: 64,
                      left: 16,
                      child: _Pill(
                          text:
                              '${_lastDetection!.label} ${_lastDetection!.directionName}',
                          color: Colors.redAccent),
                    ),
                  Positioned(
                      left: 16,
                      right: 16,
                      bottom: 16,
                      child: Text(_status,
                          style: const TextStyle(
                              color: Colors.white,
                              fontSize: 18,
                              fontWeight: FontWeight.w700))),
                ],
              ),
            ),
          ),
          const SizedBox(height: 14),
          Row(
            children: [
              Expanded(
                  child: FilledButton.icon(
                      onPressed: _toggle,
                      icon: Icon(_active ? Icons.stop : Icons.play_arrow),
                      label: Text(_active ? 'Stop' : 'Start'))),
              const SizedBox(width: 10),
              Expanded(
                  child: OutlinedButton.icon(
                      onPressed: _simulateObstacle,
                      icon: const Icon(Icons.radar),
                      label: const Text('Demo Detect'))),
            ],
          ),
        ],
      ),
    );
  }
}

// ---------------------------------------------------------------------------
// AiBenchmarkScreen
// ---------------------------------------------------------------------------

class AiBenchmarkScreen extends StatefulWidget {
  const AiBenchmarkScreen({super.key});

  @override
  State<AiBenchmarkScreen> createState() => _AiBenchmarkScreenState();
}

class _AiBenchmarkScreenState extends State<AiBenchmarkScreen> {
  static const _runsKey = 'ai_benchmark_runs';
  List<String> _models = const [];
  String _selectedModel = AppConstants.yoloModelPath;
  List<Map<String, dynamic>> _runs = const [];
  bool _running = false;

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

  Future<void> _load() async {
    final models = await _discoverTfliteModels();
    final selected = await AppConstants.getSelectedYoloModelPath();
    final prefs = await SharedPreferences.getInstance();
    final rawRuns = prefs.getStringList(_runsKey) ?? const [];
    setState(() {
      _models = models.isEmpty ? [selected] : models;
      _selectedModel = models.contains(selected) ? selected : _models.first;
      _runs = rawRuns
          .map((e) => Map<String, dynamic>.from(jsonDecode(e) as Map))
          .toList()
          .reversed
          .toList();
    });
  }

  Future<void> _setModel(String? value) async {
    if (value == null) return;
    await AppConstants.setSelectedYoloModelPath(value);
    sl<YoloDetector>().dispose();
    await sl<YoloDetector>().init();
    setState(() => _selectedModel = value);
    _snack(context, 'Model aktif: ${value.split('/').last}');
  }

  Future<void> _runBenchmark() async {
    setState(() => _running = true);
    final started = DateTime.now();
    final captureMs = await _measureCapture();

    final inferenceWatch = Stopwatch()..start();
    String label = 'person';
    String direction = 'CENTER';
    String distance = 'Demo';
    var modelLoaded = false;
    try {
      await rootBundle.load(_selectedModel).timeout(const Duration(seconds: 3));
      modelLoaded = true;
    } catch (_) {}
    final detection = await sl<YoloDetector>().detectFallback();
    if (detection != null) {
      label = detection.label;
      direction = detection.directionName;
      distance = detection.estimatedDistance;
    }
    inferenceWatch.stop();

    final notifWatch = Stopwatch()..start();
    final text = 'Obstacle $label di $direction, jarak $distance';
    notifWatch.stop();

    final ttsWatch = Stopwatch()..start();
    try {
      await sl<TtsService>()
          .speakImmediate(text)
          .timeout(const Duration(seconds: 3));
    } catch (_) {}
    ttsWatch.stop();

    final run = {
      'time': started.toIso8601String(),
      'model': _selectedModel,
      'modelLoaded': modelLoaded,
      'captureMs': captureMs,
      'inferenceMs': inferenceWatch.elapsedMilliseconds,
      'notificationMs': notifWatch.elapsedMicroseconds / 1000,
      'ttsMs': ttsWatch.elapsedMilliseconds,
      'label': label,
      'direction': direction,
    };
    final prefs = await SharedPreferences.getInstance();
    final next = [
      jsonEncode(run),
      ...((prefs.getStringList(_runsKey) ?? const []).take(24))
    ];
    await prefs.setStringList(_runsKey, next);
    if (mounted) {
      setState(() {
        _runs = [run, ..._runs].take(25).toList();
        _running = false;
      });
    }
  }

  Future<int> _measureCapture() async {
    final watch = Stopwatch()..start();
    CameraController? controller;
    try {
      final cameras =
          await availableCameras().timeout(const Duration(seconds: 3));
      if (cameras.isNotEmpty) {
        controller = CameraController(cameras.first, ResolutionPreset.low,
            enableAudio: false);
        await controller.initialize().timeout(const Duration(seconds: 5));
        await controller.takePicture().timeout(const Duration(seconds: 5));
      }
    } catch (_) {
      await Future<void>.delayed(const Duration(milliseconds: 16));
    } finally {
      await controller?.dispose();
    }
    watch.stop();
    return watch.elapsedMilliseconds;
  }

  Future<void> _clearRuns() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(_runsKey);
    setState(() => _runs = const []);
  }

  @override
  Widget build(BuildContext context) {
    final hasRealModel = _models.any((model) => model.endsWith('.tflite'));
    return _Page(
      title: 'AI Benchmark',
      subtitle: 'Capture, model, notification text, and TTS timing',
      child: ListView(
        children: [
          DropdownButtonFormField<String>(
            value: _selectedModel,
            decoration: const InputDecoration(labelText: 'Model file'),
            items: [
              for (final model in _models)
                DropdownMenuItem(
                    value: model, child: Text(model.split('/').last))
            ],
            onChanged: _setModel,
          ),
          if (!hasRealModel) ...[
            const SizedBox(height: 10),
            const _StatusBox(
              success: false,
              message:
                  'Belum ada file .tflite di assets/models. Taruh 3-5 model di folder itu, lalu restart app untuk muncul di dropdown.',
            ),
          ],
          const SizedBox(height: 12),
          FilledButton.icon(
            onPressed: _running ? null : _runBenchmark,
            icon: _running
                ? const SizedBox(
                    width: 18,
                    height: 18,
                    child: CircularProgressIndicator(strokeWidth: 2))
                : const Icon(Icons.play_arrow),
            label: Text(_running ? 'Running benchmark...' : 'Run benchmark'),
          ),
          const SizedBox(height: 8),
          OutlinedButton.icon(
              onPressed: _clearRuns,
              icon: const Icon(Icons.delete_outline),
              label: const Text('Clear log')),
          const SizedBox(height: 16),
          for (final run in _runs) _BenchmarkCard(run: run),
          if (_runs.isEmpty)
            const _EmptyPanel(
              icon: Icons.speed,
              title: 'Belum Ada Log',
              message:
                  'Klik Run benchmark untuk mencatat waktu capture, model/inference, text notification, dan TTS.',
            ),
        ],
      ),
    );
  }
}

class _BenchmarkCard extends StatelessWidget {
  final Map<String, dynamic> run;
  const _BenchmarkCard({required this.run});

  @override
  Widget build(BuildContext context) {
    final time = DateTime.tryParse(run['time']?.toString() ?? '')?.toLocal();
    return Card(
      elevation: 0,
      margin: const EdgeInsets.only(bottom: 10),
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
          side: const BorderSide(color: Color(0xFFE2E8F0))),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
                time == null
                    ? 'Benchmark run'
                    : '${_two(time.hour)}:${_two(time.minute)}:${_two(time.second)}',
                style: const TextStyle(fontWeight: FontWeight.w800)),
            const SizedBox(height: 8),
            Text(
                'Model: ${run['model'].toString().split('/').last} (${run['modelLoaded'] == true ? 'loaded' : 'fallback'})'),
            Text('Capture: ${run['captureMs']} ms'),
            Text('Model/inference: ${run['inferenceMs']} ms'),
            Text('Notification text: ${run['notificationMs']} ms'),
            Text('TTS start: ${run['ttsMs']} ms'),
            Text('Result: ${run['label']} ${run['direction']}'),
          ],
        ),
      ),
    );
  }
}

// ---------------------------------------------------------------------------
// Shared private widgets (scoped to this file)
// ---------------------------------------------------------------------------

class _Page extends StatelessWidget {
  final String title;
  final String? subtitle;
  final Widget child;
  final List<Widget>? actions;

  const _Page(
      {required this.title, required this.child, this.subtitle, this.actions});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(title,
                          style: Theme.of(context)
                              .textTheme
                              .headlineSmall
                              ?.copyWith(fontWeight: FontWeight.w800)),
                      if (subtitle != null)
                        Text(subtitle!,
                            style: const TextStyle(color: Color(0xFF64748B))),
                    ],
                  ),
                ),
                ...?actions,
              ],
            ),
            const SizedBox(height: 16),
            Expanded(child: child),
          ],
        ),
      ),
    );
  }
}

class _Pill extends StatelessWidget {
  final String text;
  final Color color;
  const _Pill({required this.text, required this.color});

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
          color: color.withValues(alpha: 0.16),
          borderRadius: BorderRadius.circular(999),
          border: Border.all(color: color)),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
        child: Text(text,
            style: TextStyle(color: color, fontWeight: FontWeight.w800)),
      ),
    );
  }
}

class _StatusBox extends StatelessWidget {
  final bool success;
  final String message;
  const _StatusBox({required this.success, required this.message});

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
        color: success ? const Color(0xFFF0FDF4) : const Color(0xFFFEF2F2),
        borderRadius: BorderRadius.circular(10),
      ),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Text(message,
            style: TextStyle(
                color: success
                    ? const Color(0xFF166534)
                    : const Color(0xFF991B1B))),
      ),
    );
  }
}

class _EmptyPanel extends StatelessWidget {
  final IconData icon;
  final String title;
  final String message;
  const _EmptyPanel(
      {required this.icon,
      required this.title,
      required this.message});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      constraints: const BoxConstraints(minHeight: 180),
      padding: const EdgeInsets.all(18),
      decoration: BoxDecoration(
          color: const Color(0xFFF8FAFC),
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: const Color(0xFFE2E8F0))),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(icon, color: const Color(0xFF64748B), size: 48),
          const SizedBox(height: 12),
          Text(title,
              style:
                  const TextStyle(fontWeight: FontWeight.w800, fontSize: 18)),
          const SizedBox(height: 6),
          Text(message, textAlign: TextAlign.center),
        ],
      ),
    );
  }
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

Future<List<String>> _discoverTfliteModels() async {
  try {
    final manifestRaw = await rootBundle.loadString('AssetManifest.json');
    final manifest = jsonDecode(manifestRaw) as Map<String, dynamic>;
    final models = manifest.keys
        .where((key) =>
            key.startsWith('assets/models/') && key.endsWith('.tflite'))
        .toList()
      ..sort();
    return models;
  } catch (_) {
    return const [];
  }
}

String _two(int value) => value.toString().padLeft(2, '0');

void _snack(BuildContext context, String message) {
  if (context.mounted) {
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text(message)));
  }
}
