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

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';

import '../../widgets/desktop_constrained.dart';
import '../../widgets/toast_helper.dart';

enum _NetworkTestTab { network, service, custom }

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

  @override
  State<NetworkTestPage> createState() => _NetworkTestPageState();
}

class _NetworkTestPageState extends State<NetworkTestPage> {
  static final _panUri = Uri.parse('https://pan.gongyun.org');
  static final _infoUri = Uri.parse('https://www.gongyun.org/appnetinfo.html');
  static final _primaryIpLookupUri = Uri.parse('https://ipwho.is/');
  static final _secondaryIpLookupUri = Uri.parse('https://ipapi.co/json/');
  static const _probeHeaders = {'Cache-Control': 'no-cache'};
  static const _probeGetFallbackHeaders = {
    'Cache-Control': 'no-cache',
    'Range': 'bytes=0-0',
  };

  _NetworkTestTab _tab = _NetworkTestTab.network;
  bool _networkLoading = false;
  bool _serviceLoading = false;
  _NetworkDiagnostics? _networkDiagnostics;
  _ServiceDiagnostics? _serviceDiagnostics;
  String? _networkError;
  String? _serviceError;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => _runNetworkTest());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('网络测试'),
        actions: [
          IconButton(
            tooltip: '说明',
            icon: const Icon(Icons.info_outline),
            onPressed: () => _openExternalUrl(_infoUri),
          ),
        ],
      ),
      body: DesktopConstrained(
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
              child: SizedBox(
                width: double.infinity,
                child: SegmentedButton<_NetworkTestTab>(
                  segments: const [
                    ButtonSegment(
                      value: _NetworkTestTab.network,
                      icon: Icon(Icons.public),
                      label: Text('网络'),
                    ),
                    ButtonSegment(
                      value: _NetworkTestTab.service,
                      icon: Icon(Icons.dns_outlined),
                      label: Text('服务'),
                    ),
                    ButtonSegment(
                      value: _NetworkTestTab.custom,
                      icon: Icon(Icons.route_outlined),
                      label: Text('自定义线路'),
                    ),
                  ],
                  selected: {_tab},
                  onSelectionChanged: (selection) {
                    final selected = selection.first;
                    if (selected == _NetworkTestTab.custom) {
                      _showCustomUnavailableDialog();
                      return;
                    }
                    setState(() => _tab = selected);
                    if (selected == _NetworkTestTab.network &&
                        _networkDiagnostics == null &&
                        !_networkLoading) {
                      _runNetworkTest();
                    }
                    if (selected == _NetworkTestTab.service &&
                        _serviceDiagnostics == null &&
                        !_serviceLoading) {
                      _runServiceTest();
                    }
                  },
                ),
              ),
            ),
            Expanded(
              child: AnimatedSwitcher(
                duration: const Duration(milliseconds: 180),
                child: switch (_tab) {
                  _NetworkTestTab.network => _NetworkPanel(
                    key: const ValueKey('network'),
                    loading: _networkLoading,
                    error: _networkError,
                    diagnostics: _networkDiagnostics,
                    onRefresh: _runNetworkTest,
                  ),
                  _NetworkTestTab.service => _ServicePanel(
                    key: const ValueKey('service'),
                    loading: _serviceLoading,
                    error: _serviceError,
                    diagnostics: _serviceDiagnostics,
                    onRefresh: _runServiceTest,
                  ),
                  _NetworkTestTab.custom => const SizedBox.shrink(),
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _showCustomUnavailableDialog() async {
    await showDialog<void>(
      context: context,
      builder: (dialogContext) => AlertDialog(
        title: const Text('自定义线路'),
        content: const Text('自定义线路为会员功能，当前版本暂未开放。'),
        actions: [
          FilledButton(
            onPressed: () => Navigator.of(dialogContext).pop(),
            child: const Text('好'),
          ),
        ],
      ),
    );
  }

  Future<void> _openExternalUrl(Uri uri) async {
    try {
      final opened = await launchUrl(uri, mode: LaunchMode.externalApplication);
      if (!opened) {
        ToastHelper.failure('无法打开链接: $uri');
      }
    } catch (e) {
      ToastHelper.failure('无法打开链接: $e');
    }
  }

  Future<void> _runNetworkTest() async {
    if (_networkLoading) return;

    setState(() {
      _networkLoading = true;
      _networkError = null;
    });

    try {
      final ipFuture = _fetchIpInfo();
      final panProbe = await _probeUri(_panUri);
      final stability = await _testStability(_panUri);
      final ipInfo = await ipFuture;

      if (!mounted) return;
      setState(() {
        _networkDiagnostics = _NetworkDiagnostics(
          panReachable: panProbe.reachable,
          latencyMs: panProbe.latencyMs,
          ipInfo: ipInfo,
          stable: stability.stable,
          sampleCount: stability.sampleCount,
          successCount: stability.successCount,
        );
      });
    } catch (e) {
      if (mounted) setState(() => _networkError = e.toString());
    } finally {
      if (mounted) setState(() => _networkLoading = false);
    }
  }

  Future<void> _runServiceTest() async {
    if (_serviceLoading) return;

    setState(() {
      _serviceLoading = true;
      _serviceError = null;
    });

    try {
      final monitorHeartbeats = await _fetchMonitorHeartbeats('gongyun');

      _ServiceItem monitor(String label, int monitorId) {
        return _monitorItem(
          label: label,
          monitorId: monitorId,
          heartbeats: monitorHeartbeats,
        );
      }

      final telegramBot = await _probeItem(
        label: 'Telegram bot',
        uri: Uri.parse('https://bot.mygongyun.com'),
        healthyStatusCodes: const {200, 401},
      );

      final business = <_ServiceItem>[
        monitor('官网', 3),
        monitor('网盘', 2),
        monitor('短链', 37),
        monitor('SSO', 39),
        telegramBot,
      ];

      final dc = <_ServiceItem>[
        monitor('DC3-CN-SZ', 13),
        monitor('DC3-CN-MIAMI', 11),
        monitor('DC3-CN-NY', 12),
        monitor('DC3-CN-LV', 10),
      ];

      final backend = <_ServiceItem>[monitor('数据库', 32), monitor('键值对数据库', 36)];

      final storage = <_ServiceItem>[
        monitor('DC-S-NA-1', 5),
        monitor('DC-S-NA-2', 6),
        monitor('DC-S-EU', 7),
        monitor('DC-S-EP', 8),
      ];

      final acceleration = <_ServiceItem>[
        monitor('香港', 18),
        monitor('香港', 19),
        monitor('新加坡', 22),
        monitor('洛杉矶', 24),
        monitor('蒙特利尔', 21),
      ];

      if (!mounted) return;
      setState(() {
        _serviceDiagnostics = _ServiceDiagnostics(
          fetchedAt: DateTime.now(),
          business: business,
          dc: dc,
          backend: backend,
          storage: storage,
          acceleration: acceleration,
        );
      });
    } catch (e) {
      if (mounted) setState(() => _serviceError = e.toString());
    } finally {
      if (mounted) setState(() => _serviceLoading = false);
    }
  }

  Future<_IpInfo> _fetchIpInfo() async {
    try {
      final response = await http
          .get(_primaryIpLookupUri)
          .timeout(const Duration(seconds: 8));
      if (response.statusCode >= 200 && response.statusCode < 300) {
        final data = jsonDecode(response.body) as Map<String, dynamic>;
        if (data['success'] != false) {
          final connection = data['connection'];
          final connectionMap = connection is Map<String, dynamic>
              ? connection
              : <String, dynamic>{};
          return _IpInfo(
            ip: data['ip']?.toString() ?? '未知',
            asn: connectionMap['asn']?.toString() ?? '未知',
            operatorName: connectionMap['isp']?.toString().isNotEmpty == true
                ? connectionMap['isp'].toString()
                : (connectionMap['org']?.toString() ?? '未知'),
          );
        }
      }
    } catch (_) {
      // Fall back to the next public endpoint.
    }

    try {
      final response = await http
          .get(_secondaryIpLookupUri)
          .timeout(const Duration(seconds: 8));
      if (response.statusCode >= 200 && response.statusCode < 300) {
        final data = jsonDecode(response.body) as Map<String, dynamic>;
        return _IpInfo(
          ip: data['ip']?.toString() ?? '未知',
          asn: data['asn']?.toString() ?? '未知',
          operatorName: data['org']?.toString() ?? '未知',
        );
      }
    } catch (_) {
      // Handled by the default value below.
    }

    return const _IpInfo(ip: '未知', asn: '未知', operatorName: '未知');
  }

  _ServiceItem _monitorItem({
    required String label,
    required int monitorId,
    required Map<int, _MonitorHeartbeat> heartbeats,
  }) {
    final heartbeat = heartbeats[monitorId];
    return _ServiceItem(
      label: label,
      online: heartbeat?.online ?? false,
      latencyMs: heartbeat?.pingMs,
    );
  }

  Future<Map<int, _MonitorHeartbeat>> _fetchMonitorHeartbeats(
    String pageId,
  ) async {
    try {
      final response = await http
          .get(
            Uri.parse('https://status.gongyun.org/api/monitor?pageId=$pageId'),
            headers: _probeHeaders,
          )
          .timeout(const Duration(seconds: 10));
      if (response.statusCode < 200 || response.statusCode >= 300) {
        return const {};
      }

      final decoded = jsonDecode(response.body);
      if (decoded is! Map<String, dynamic>) return const {};
      final data = decoded['data'];
      if (data is! Map<String, dynamic>) return const {};
      final heartbeatList = data['heartbeatList'];
      if (heartbeatList is! Map<String, dynamic>) return const {};

      final result = <int, _MonitorHeartbeat>{};
      for (final entry in heartbeatList.entries) {
        final monitorId = int.tryParse(entry.key);
        final records = entry.value;
        if (monitorId == null || records is! List || records.isEmpty) continue;
        final latest = records.last;
        if (latest is! Map<String, dynamic>) continue;
        final status = _asInt(latest['status']);
        final ping = _asInt(latest['ping']);
        result[monitorId] = _MonitorHeartbeat(
          online: status == 1,
          pingMs: ping,
        );
      }

      return result;
    } catch (_) {
      return const {};
    }
  }

  int? _asInt(Object? value) {
    if (value is int) return value;
    if (value is num) return value.round();
    return int.tryParse(value?.toString() ?? '');
  }

  Future<_ServiceItem> _probeItem({
    required String label,
    required Uri uri,
    Set<int> healthyStatusCodes = const {},
  }) async {
    final probe = await _probeUri(uri, healthyStatusCodes: healthyStatusCodes);
    return _ServiceItem(
      label: label,
      online: probe.reachable,
      latencyMs: probe.latencyMs,
    );
  }

  Future<_ProbeResult> _probeUri(
    Uri uri, {
    Set<int> healthyStatusCodes = const {},
  }) async {
    final stopwatch = Stopwatch()..start();
    try {
      var response = await http
          .head(uri, headers: _probeHeaders)
          .timeout(const Duration(seconds: 8));
      if (_shouldRetryProbeWithGet(response.statusCode)) {
        response = await http
            .get(uri, headers: _probeGetFallbackHeaders)
            .timeout(const Duration(seconds: 8));
      }
      stopwatch.stop();
      final isHealthy = _isHealthyStatus(
        response.statusCode,
        healthyStatusCodes,
      );
      return _ProbeResult(
        reachable: isHealthy,
        latencyMs: stopwatch.elapsedMilliseconds,
      );
    } catch (_) {
      stopwatch.stop();
      return const _ProbeResult(reachable: false);
    }
  }

  bool _shouldRetryProbeWithGet(int statusCode) {
    return statusCode == 403 || statusCode == 405 || statusCode == 501;
  }

  bool _isHealthyStatus(int statusCode, Set<int> healthyStatusCodes) {
    if (healthyStatusCodes.isNotEmpty) {
      return healthyStatusCodes.contains(statusCode);
    }
    return statusCode >= 200 && statusCode < 500;
  }

  Future<_StabilityResult> _testStability(Uri uri) async {
    final latencies = <int>[];
    var successCount = 0;
    const sampleCount = 3;

    for (var i = 0; i < sampleCount; i++) {
      final probe = await _probeUri(uri);
      if (probe.reachable) {
        successCount += 1;
        if (probe.latencyMs != null) latencies.add(probe.latencyMs!);
      }
      if (i < sampleCount - 1) {
        await Future<void>.delayed(const Duration(milliseconds: 350));
      }
    }

    final jitter = latencies.isEmpty
        ? 999999
        : latencies.reduce(max) - latencies.reduce(min);
    return _StabilityResult(
      stable: successCount == sampleCount && jitter < 1500,
      sampleCount: sampleCount,
      successCount: successCount,
    );
  }
}

class _NetworkPanel extends StatelessWidget {
  final bool loading;
  final String? error;
  final _NetworkDiagnostics? diagnostics;
  final VoidCallback onRefresh;

  const _NetworkPanel({
    super.key,
    required this.loading,
    required this.error,
    required this.diagnostics,
    required this.onRefresh,
  });

  @override
  Widget build(BuildContext context) {
    final data = diagnostics;
    final errorColor = Theme.of(context).colorScheme.error;
    return ListView(
      padding: const EdgeInsets.fromLTRB(16, 4, 16, 18),
      children: [
        _SectionCard(
          title: '网络',
          children: [
            if (loading) const LinearProgressIndicator(),
            if (error != null) _ErrorText(error!),
            _InfoRow(
              label: '公云存储网盘访问测试',
              value: data == null ? '检测中' : (data.panReachable ? '正常' : '离线'),
              valueColor: _statusColor(context, data?.panReachable),
            ),
            _InfoRow(label: '当前网络 IP 地址', value: data?.ipInfo.ip ?? '检测中'),
            _InfoRow(label: 'IP 归属 AS', value: data?.ipInfo.asn ?? '检测中'),
            _InfoRow(label: '运营商', value: data?.ipInfo.operatorName ?? '检测中'),
            _InfoRow(
              label: '连接时延',
              value: data?.latencyMs == null ? '检测中' : '${data!.latencyMs} ms',
            ),
            _InfoRow(
              label: '长期连接稳定性',
              value: data == null
                  ? '检测中'
                  : '${data.stable ? '稳定' : '不稳定'} '
                        '(${data.successCount}/${data.sampleCount})',
              valueColor: _statusColor(context, data?.stable),
            ),
          ],
        ),
        _SectionCard(
          title: '说明',
          children: const [
            _DescriptionLine('公云存储作为国际化云存储产品，已接入Cloudflare CDN作为全球加速引擎。'),
            _DescriptionLine(
              '当前网络 IP 地址、AS 与运营商信息通过第三方服务 ipwho.is 查询；如该服务不可用，将使用 ipapi.co 作为备用查询。',
            ),
            _DescriptionLine(
              '由于部分国家/地区有法律法规限制与审查，可能会出现包括但不限于网络波动、网络丢包以及网络连接不稳定现象',
            ),
            _DescriptionLine(
              '如若出现网络连接问题，您可将本页面内容向公云存储团队报告，团队会根据相关诊断线索进行排查与优化。',
            ),
          ],
        ),
        _SectionCard(
          title: '警告',
          titleColor: errorColor,
          children: [
            Text(
              '本页面内容涉及你本人敏感信息请勿提供给非公云存储团队人员，以避免你本人相关信息泄露！',
              style: TextStyle(color: errorColor, fontWeight: FontWeight.w700),
            ),
          ],
        ),
        const SizedBox(height: 8),
        FilledButton.icon(
          onPressed: loading ? null : onRefresh,
          icon: const Icon(Icons.refresh),
          label: const Text('重新检测'),
        ),
      ],
    );
  }
}

class _ServicePanel extends StatelessWidget {
  final bool loading;
  final String? error;
  final _ServiceDiagnostics? diagnostics;
  final VoidCallback onRefresh;

  const _ServicePanel({
    super.key,
    required this.loading,
    required this.error,
    required this.diagnostics,
    required this.onRefresh,
  });

  @override
  Widget build(BuildContext context) {
    final data = diagnostics;
    return ListView(
      padding: const EdgeInsets.fromLTRB(16, 4, 16, 18),
      children: [
        _SectionCard(
          title: '业务状态',
          children: [
            if (loading) const LinearProgressIndicator(),
            if (error != null) _ErrorText(error!),
            ..._itemsOrLoading(data?.business),
          ],
        ),
        _SectionCard(title: '离线下载与计算节点', children: _itemsOrLoading(data?.dc)),
        _SectionCard(title: '后端', children: _itemsOrLoading(data?.backend)),
        _SectionCard(title: '存储节点', children: _itemsOrLoading(data?.storage)),
        _SectionCard(
          title: '加速线路',
          children: _itemsOrLoading(data?.acceleration, showLatency: true),
        ),
        const SizedBox(height: 8),
        FilledButton.icon(
          onPressed: loading ? null : onRefresh,
          icon: const Icon(Icons.refresh),
          label: const Text('重新检测'),
        ),
      ],
    );
  }

  List<Widget> _itemsOrLoading(
    List<_ServiceItem>? items, {
    bool showLatency = false,
  }) {
    if (items == null) {
      return const [_InfoRow(label: '状态', value: '检测中')];
    }
    return items
        .map(
          (item) => _InfoRow(
            label: item.label,
            value: showLatency
                ? item.latencyLabel
                : (item.online ? '正常' : '离线'),
            valueColor: item.online ? Colors.green : Colors.red,
          ),
        )
        .toList();
  }
}

class _SectionCard extends StatelessWidget {
  final String title;
  final List<Widget> children;
  final Color? titleColor;

  const _SectionCard({
    required this.title,
    required this.children,
    this.titleColor,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Card(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(16, 14, 16, 12),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                title,
                style: Theme.of(context).textTheme.titleSmall?.copyWith(
                  color: titleColor,
                  fontWeight: FontWeight.w800,
                ),
              ),
              const SizedBox(height: 10),
              ...children,
            ],
          ),
        ),
      ),
    );
  }
}

class _InfoRow extends StatelessWidget {
  final String label;
  final String value;
  final Color? valueColor;

  const _InfoRow({required this.label, required this.value, this.valueColor});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 6),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(child: Text(label, style: theme.textTheme.bodyMedium)),
          const SizedBox(width: 16),
          Flexible(
            child: Text(
              value,
              textAlign: TextAlign.right,
              style: theme.textTheme.bodyMedium?.copyWith(
                color: valueColor,
                fontWeight: FontWeight.w700,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _DescriptionLine extends StatelessWidget {
  final String text;

  const _DescriptionLine(this.text);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 5),
      child: Text(text),
    );
  }
}

class _ErrorText extends StatelessWidget {
  final String error;

  const _ErrorText(this.error);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: Text(
        '检测失败：$error',
        style: TextStyle(color: Theme.of(context).colorScheme.error),
      ),
    );
  }
}

Color? _statusColor(BuildContext context, bool? value) {
  if (value == null) return null;
  return value ? Colors.green : Theme.of(context).colorScheme.error;
}

class _NetworkDiagnostics {
  final bool panReachable;
  final int? latencyMs;
  final _IpInfo ipInfo;
  final bool stable;
  final int sampleCount;
  final int successCount;

  const _NetworkDiagnostics({
    required this.panReachable,
    required this.latencyMs,
    required this.ipInfo,
    required this.stable,
    required this.sampleCount,
    required this.successCount,
  });
}

class _IpInfo {
  final String ip;
  final String asn;
  final String operatorName;

  const _IpInfo({
    required this.ip,
    required this.asn,
    required this.operatorName,
  });
}

class _ServiceDiagnostics {
  final DateTime fetchedAt;
  final List<_ServiceItem> business;
  final List<_ServiceItem> dc;
  final List<_ServiceItem> backend;
  final List<_ServiceItem> storage;
  final List<_ServiceItem> acceleration;

  const _ServiceDiagnostics({
    required this.fetchedAt,
    required this.business,
    required this.dc,
    required this.backend,
    required this.storage,
    required this.acceleration,
  });
}

class _ServiceItem {
  final String label;
  final bool online;
  final int? latencyMs;

  const _ServiceItem({
    required this.label,
    required this.online,
    this.latencyMs,
  });

  String get latencyLabel {
    if (!online) return '离线';
    if (latencyMs == null) return '正常 / -- ms';
    return '正常 / $latencyMs ms';
  }
}

class _MonitorHeartbeat {
  final bool online;
  final int? pingMs;

  const _MonitorHeartbeat({required this.online, this.pingMs});
}

class _ProbeResult {
  final bool reachable;
  final int? latencyMs;

  const _ProbeResult({required this.reachable, this.latencyMs});
}

class _StabilityResult {
  final bool stable;
  final int sampleCount;
  final int successCount;

  const _StabilityResult({
    required this.stable,
    required this.sampleCount,
    required this.successCount,
  });
}
