import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../../models/adb_device.dart';
import '../../services/adb_service.dart';

class ConsolePage extends StatefulWidget {
  const ConsolePage({super.key, this.device, this.logcatStarter});

  final AdbDevice? device;
  final AdbLogcatSessionStarter? logcatStarter;

  @override
  State<ConsolePage> createState() => _ConsolePageState();
}

class _ConsolePageState extends State<ConsolePage> {
  final List<String> _logLines = [];
  final TextEditingController _filterController = TextEditingController();
  AdbLogcatSession? _session;
  StreamSubscription<String>? _subscription;
  bool _isLoading = false;
  String? _errorMsg;
  int _sessionGeneration = 0;
  final ScrollController _scrollController = ScrollController();
  bool _isPaused = false;
  _LogcatLevelFilter _levelFilter = _LogcatLevelFilter.all;

  void _scrollToBottom() {
    if (_scrollController.hasClients) {
      _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (_scrollController.hasClients && !_isPaused) {
          _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
        }
      });
    }
  }

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

  @override
  void didUpdateWidget(ConsolePage oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.device != oldWidget.device ||
        widget.logcatStarter != oldWidget.logcatStarter) {
      _stopSession();
      _logLines.clear();
      _isPaused = false;
      _startSession();
    }
  }

  @override
  void dispose() {
    _stopSession();
    _filterController.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  Future<void> _startSession() async {
    final starter = widget.logcatStarter;
    final device = widget.device;
    if (starter == null || device == null) {
      _isLoading = false;
      _errorMsg = null;
      return;
    }

    _sessionGeneration++;
    final currentGen = _sessionGeneration;

    setState(() {
      _isLoading = true;
      _errorMsg = null;
    });

    try {
      final session = await starter(serial: device.serial);
      if (currentGen != _sessionGeneration) {
        await session.stop();
        return;
      }
      if (!mounted) {
        await session.stop();
        return;
      }
      _session = session;
      _subscription = session.lines.listen(
        (line) {
          if (!mounted || currentGen != _sessionGeneration) return;
          setState(() {
            _logLines.add(line);
            if (_logLines.length > 1000) {
              _logLines.removeRange(0, _logLines.length - 1000);
            }
          });
          if (!_isPaused) {
            WidgetsBinding.instance.addPostFrameCallback((_) {
              if (mounted && currentGen == _sessionGeneration) {
                _scrollToBottom();
              }
            });
          }
        },
        onError: (err) {
          if (!mounted || currentGen != _sessionGeneration) return;
          setState(() {
            _errorMsg = err.toString();
          });
        },
      );
      setState(() {
        _isLoading = false;
      });
    } catch (e) {
      if (!mounted || currentGen != _sessionGeneration) return;
      setState(() {
        _isLoading = false;
        _errorMsg = e.toString();
      });
    }
  }

  void _stopSession() {
    _sessionGeneration++;
    _subscription?.cancel();
    _subscription = null;
    _session?.stop();
    _session = null;
  }

  List<String> get _displayLines {
    final sourceLines = (widget.device == null || widget.logcatStarter == null)
        ? _sampleLogLines
        : _logLines;

    return sourceLines
        .where(_matchesLevelFilter)
        .where(_matchesQueryFilter)
        .toList(growable: false);
  }

  bool _matchesLevelFilter(String line) {
    return switch (_levelFilter) {
      _LogcatLevelFilter.all => true,
      _LogcatLevelFilter.debug =>
        _LogcatLevel.fromLine(line) == _LogcatLevel.debug,
      _LogcatLevelFilter.info =>
        _LogcatLevel.fromLine(line) == _LogcatLevel.info,
      _LogcatLevelFilter.warn =>
        _LogcatLevel.fromLine(line) == _LogcatLevel.warn,
      _LogcatLevelFilter.error =>
        _LogcatLevel.fromLine(line) == _LogcatLevel.error,
    };
  }

  bool _matchesQueryFilter(String line) {
    final query = _filterController.text.trim().toLowerCase();
    if (query.isEmpty) return true;

    return line.toLowerCase().contains(query);
  }

  bool get _hasLiveLogs =>
      widget.device != null &&
      widget.logcatStarter != null &&
      _displayLines.isNotEmpty;

  Future<void> _copyVisibleLogsToClipboard() async {
    if (widget.device == null || widget.logcatStarter == null) return;
    final lines = _displayLines;
    if (lines.isEmpty) return;

    await Clipboard.setData(ClipboardData(text: lines.join('\n')));
    if (!mounted) return;
    ScaffoldMessenger.of(
      context,
    ).showSnackBar(SnackBar(content: Text('로그 ${lines.length}줄 복사됨')));
  }

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Padding(
      padding: const EdgeInsets.fromLTRB(28, 0, 28, 28),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          if (widget.device != null) ...[
            Padding(
              padding: const EdgeInsets.only(bottom: 8),
              child: Row(
                key: const ValueKey('console-selected-device-label'),
                children: [
                  const Icon(Icons.phone_android, size: 18),
                  const SizedBox(width: 8),
                  Text(
                    '${widget.device!.displayName} (${widget.device!.serial})',
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                ],
              ),
            ),
          ] else ...[
            const Padding(
              padding: EdgeInsets.only(bottom: 8),
              child: Row(
                key: ValueKey('console-no-device-label'),
                children: [
                  Icon(Icons.phone_android, size: 18, color: Colors.grey),
                  SizedBox(width: 8),
                  Text('device 미선택', style: TextStyle(color: Colors.grey)),
                ],
              ),
            ),
          ],
          Wrap(
            spacing: 12,
            runSpacing: 8,
            crossAxisAlignment: WrapCrossAlignment.center,
            children: [
              ConstrainedBox(
                constraints: const BoxConstraints(minWidth: 160, maxWidth: 320),
                child: TextField(
                  controller: _filterController,
                  onChanged: (_) => setState(() {}),
                  decoration: InputDecoration(
                    labelText: '패키지, 태그 또는 텍스트',
                    prefixIcon: const Icon(Icons.filter_alt),
                    suffixIcon: IconButton(
                      tooltip: '필터 초기화',
                      icon: const Icon(Icons.close),
                      onPressed: () {
                        _filterController.clear();
                        setState(() {});
                      },
                    ),
                  ),
                ),
              ),
              SegmentedButton<_LogcatLevelFilter>(
                segments: const [
                  ButtonSegment(
                    value: _LogcatLevelFilter.all,
                    label: Text('ALL'),
                  ),
                  ButtonSegment(
                    value: _LogcatLevelFilter.debug,
                    label: Text('DEBUG'),
                  ),
                  ButtonSegment(
                    value: _LogcatLevelFilter.info,
                    label: Text('INFO'),
                  ),
                  ButtonSegment(
                    value: _LogcatLevelFilter.warn,
                    label: Text('WARN'),
                  ),
                  ButtonSegment(
                    value: _LogcatLevelFilter.error,
                    label: Text('ERROR'),
                  ),
                ],
                selected: {_levelFilter},
                onSelectionChanged: (selection) {
                  setState(() {
                    _levelFilter = selection.single;
                  });
                },
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  IconButton.filledTonal(
                    tooltip: _isPaused ? '재개' : '일시정지',
                    onPressed: () {
                      setState(() {
                        _isPaused = !_isPaused;
                        if (!_isPaused) {
                          WidgetsBinding.instance.addPostFrameCallback(
                            (_) => _scrollToBottom(),
                          );
                        }
                      });
                    },
                    icon: Icon(_isPaused ? Icons.play_arrow : Icons.pause),
                  ),
                  const SizedBox(width: 8),
                  IconButton.outlined(
                    tooltip: '지우기',
                    onPressed: () {
                      setState(_logLines.clear);
                    },
                    icon: const Icon(Icons.delete_outline),
                  ),
                  const SizedBox(width: 8),
                  IconButton.outlined(
                    tooltip: '로그 복사',
                    onPressed: _hasLiveLogs
                        ? _copyVisibleLogsToClipboard
                        : null,
                    icon: const Icon(Icons.content_copy),
                  ),
                ],
              ),
            ],
          ),
          const SizedBox(height: 18),
          Expanded(
            child: DecoratedBox(
              decoration: BoxDecoration(
                color: const Color(0xFF151A18),
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: colorScheme.outlineVariant),
              ),
              child: _isLoading
                  ? const Center(child: CircularProgressIndicator())
                  : _errorMsg != null
                  ? Center(
                      child: Text(
                        '에러 발생: $_errorMsg',
                        style: TextStyle(color: colorScheme.error),
                      ),
                    )
                  : ListView.builder(
                      controller: _scrollController,
                      padding: const EdgeInsets.all(16),
                      itemCount: _displayLines.length,
                      itemBuilder: (context, index) {
                        final line = _displayLines[index];

                        return Padding(
                          padding: const EdgeInsets.symmetric(vertical: 3),
                          child: Text(
                            line,
                            style: TextStyle(
                              color: _lineColor(line),
                              fontFamily: 'Menlo',
                              fontSize: 13,
                              height: 1.4,
                            ),
                          ),
                        );
                      },
                    ),
            ),
          ),
        ],
      ),
    );
  }

  Color _lineColor(String line) {
    return switch (_LogcatLevel.fromLine(line)) {
      _LogcatLevel.debug => const Color(0xFF8DE1D7),
      _LogcatLevel.info => const Color(0xFFBDEFA8),
      _LogcatLevel.warn => const Color(0xFFFFD28A),
      _LogcatLevel.error => const Color(0xFFFFA3A3),
      null => const Color(0xFFE6EEE9),
    };
  }
}

enum _LogcatLevel {
  debug,
  info,
  warn,
  error;

  static _LogcatLevel? fromLine(String line) {
    final level =
        RegExp(r'\s([DIWEF])\s').firstMatch(line)?.group(1) ??
        RegExp(r'(?:^|\s)([DIWEF])\/').firstMatch(line)?.group(1);

    return switch (level) {
      'D' => _LogcatLevel.debug,
      'I' => _LogcatLevel.info,
      'W' => _LogcatLevel.warn,
      'E' || 'F' => _LogcatLevel.error,
      _ => null,
    };
  }
}

enum _LogcatLevelFilter { all, debug, info, warn, error }

const _sampleLogLines = [
  '06-08 10:41:22.121 1832 2110 I ActivityTaskManager: START u0 com.toki.app/.MainActivity',
  '06-08 10:41:22.339 5521 5521 D AppSokInstall: install session committed',
  '06-08 10:41:22.921 5521 5540 W NetworkMonitor: vpn route not available, using usb install cache',
  '06-08 10:41:23.105 5521 5540 I ArtifactStore: app-debug.apk verified',
  '06-08 10:41:23.411 5521 5521 E Sample: placeholder error row for filter styling',
];
