import 'dart:io';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:network_proxy/ui/component/json/theme.dart';

class JsonText extends StatelessWidget {
  final ColorTheme colorTheme;
  final dynamic json;
  final String indent;
  final ScrollController? scrollController;

  const JsonText({super.key, required this.json, this.indent = '  ', required this.colorTheme, this.scrollController});

  @override
  Widget build(BuildContext context) {
    var jsnParser = JsnParser(json, colorTheme, indent);
    var future =
        compute((message) => message.getJsonTree(), jsnParser).catchError((error) => <Text>[Text(error.toString())]);

    return FutureBuilder(
        future: future,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
            var textList = snapshot.data as List<Text>;

            Widget widget;
            if (textList.length < 2000) {
              widget = Column(crossAxisAlignment: CrossAxisAlignment.start, children: textList);
            } else {
              widget = SizedBox(
                  width: double.infinity,
                  height: MediaQuery.of(context).size.height - 160,
                  child: ListView.builder(
                      physics: const BouncingScrollPhysics(),
                      controller: trackingScroll(),
                      cacheExtent: 1000,
                      itemBuilder: (context, index) => textList[index],
                      itemCount: textList.length));
            }
            return SelectionArea(child: widget);
          }

          return Container();
        });
  }

  ///滚动条
  ScrollController trackingScroll() {
    var trackingScroll = TrackingScrollController();
    double offset = 0;
    trackingScroll.addListener(() {
      if (trackingScroll.offset < -10 || (trackingScroll.offset < 30 && trackingScroll.offset < offset)) {
        if (scrollController != null && scrollController!.offset >= 0) {
          scrollController?.jumpTo(scrollController!.offset - max((offset - trackingScroll.offset), 15));
        }
      }
      offset = trackingScroll.offset;
    });

    if (Platform.isIOS) {
      scrollController?.addListener(() {
        if (scrollController!.offset >= scrollController!.position.maxScrollExtent) {
          scrollController?.jumpTo(scrollController!.position.maxScrollExtent);
          trackingScroll
              .jumpTo(trackingScroll.offset + (scrollController!.offset - scrollController!.position.maxScrollExtent));
        }
      });
    }

    return trackingScroll;
  }
}

class JsnParser {
  final dynamic json;
  final ColorTheme colorTheme;
  final String indent;

  JsnParser(this.json, this.colorTheme, this.indent);

  List<Text> getJsonTree() {
    List<Text> textList = [];
    if (json is Map) {
      textList.add(const Text('{'));
      textList.addAll(getMapText(json, prefix: indent));
    } else if (json is List) {
      textList.add(const Text('['));
      textList.addAll(getArrayText(json));
    } else {
      textList.add(Text(json == null ? '' : json.toString()));
    }
    return textList;
  }

  /// 获取Map json
  List<Text> getMapText(Map<String, dynamic> map, {String openPrefix = '', String prefix = '', String suffix = ''}) {
    var result = <Text>[];
    // result.add(Text('$openPrefix{'));

    var entries = map.entries;
    for (int i = 0; i < entries.length; i++) {
      var entry = entries.elementAt(i);
      String postfix = '${i == entries.length - 1 ? '' : ','} ';

      var textSpan = TextSpan(text: prefix, children: [
        TextSpan(text: '"${entry.key}"', style: TextStyle(color: colorTheme.propertyKey)),
        const TextSpan(text: ': '),
        getBasicValue(entry.value, postfix),
      ]);
      result.add(Text.rich(textSpan));

      if (entry.value is Map<String, dynamic>) {
        result.addAll(getMapText(entry.value, openPrefix: prefix, prefix: '$prefix$indent', suffix: postfix));
      } else if (entry.value is List) {
        result.addAll(getArrayText(entry.value, openPrefix: prefix, prefix: '$prefix$indent', suffix: postfix));
      }
    }

    result.add(Text('$openPrefix}$suffix'));
    return result;
  }

  /// 获取数组json
  List<Text> getArrayText(List<dynamic> list, {String openPrefix = '', String prefix = '', String suffix = ''}) {
    var result = <Text>[];
    // result.add(Text('$openPrefix['));

    for (int i = 0; i < list.length; i++) {
      var value = list[i];
      String postfix = i == list.length - 1 ? '' : ',';

      result.add(Text.rich(getBasicValue(value, postfix, prefix: prefix)));

      if (value is Map<String, dynamic>) {
        result.addAll(getMapText(value, openPrefix: '$openPrefix ', prefix: '$prefix$indent', suffix: postfix));
      } else if (value is List) {
        result.addAll(getArrayText(value, openPrefix: '$openPrefix ', prefix: '$prefix$indent', suffix: postfix));
      }
    }

    result.add(Text('$openPrefix]$suffix'));
    return result;
  }

  /// 获取基本类型值 复杂类型会忽略
  InlineSpan getBasicValue(dynamic value, String suffix, {String? prefix}) {
    if (value == null) {
      return TextSpan(
          text: prefix,
          children: [TextSpan(text: 'null', style: TextStyle(color: colorTheme.keyword)), TextSpan(text: suffix)]);
    }

    if (value is String) {
      return TextSpan(
          text: prefix,
          children: [TextSpan(text: '"$value"', style: TextStyle(color: colorTheme.string)), TextSpan(text: suffix)]);
    }

    if (value is num) {
      return TextSpan(text: prefix, children: [
        TextSpan(text: value.toString(), style: TextStyle(color: colorTheme.number)),
        TextSpan(text: suffix)
      ]);
    }

    if (value is bool) {
      return TextSpan(text: prefix, children: [
        TextSpan(text: value.toString(), style: TextStyle(color: colorTheme.keyword)),
        TextSpan(text: suffix)
      ]);
    }

    if (value is List) {
      return TextSpan(text: "${prefix ?? ''}[");
    }

    return TextSpan(text: "${prefix ?? ''}{");
  }
}
