// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';

import 'semantics_tester.dart';

void main() {
  testWidgetsWithLeakTracking('SemanticsNode ids are stable', (WidgetTester tester) async {
    // Regression test for b/151732341.
    final SemanticsTester semantics = SemanticsTester(tester);
    final TapGestureRecognizer recognizer1 = TapGestureRecognizer();
    addTearDown(recognizer1.dispose);
    final TapGestureRecognizer recognizer2 = TapGestureRecognizer();
    addTearDown(recognizer2.dispose);
    final TapGestureRecognizer recognizer3 = TapGestureRecognizer();
    addTearDown(recognizer3.dispose);

    await tester.pumpWidget(Directionality(
    textDirection: TextDirection.ltr,
      child: Text.rich(
        TextSpan(
          text: 'Hallo ',
          recognizer: recognizer1..onTap = () {},
          children: <TextSpan>[
            TextSpan(
              text: 'Welt ',
              recognizer: recognizer2..onTap = () {},
            ),
            TextSpan(
              text: '!!!',
              recognizer: recognizer3..onTap = () {},
            ),
          ],
        ),
      ),
    ));
    expect(find.text('Hallo Welt !!!'), findsOneWidget);
    final SemanticsNode node = tester.getSemantics(find.text('Hallo Welt !!!'));
    final Map<String, int> labelToNodeId = <String, int>{};
    node.visitChildren((SemanticsNode node) {
      labelToNodeId[node.label] = node.id;
       return true;
    });
    expect(node.id, 1);
    expect(labelToNodeId['Hallo '], 2);
    expect(labelToNodeId['Welt '], 3);
    expect(labelToNodeId['!!!'], 4);
    expect(labelToNodeId.length, 3);

    // Rebuild semantics.
    tester.renderObject(find.text('Hallo Welt !!!')).markNeedsSemanticsUpdate();
    await tester.pump();

    final SemanticsNode nodeAfterRebuild = tester.getSemantics(find.text('Hallo Welt !!!'));
    final Map<String, int> labelToNodeIdAfterRebuild = <String, int>{};
    nodeAfterRebuild.visitChildren((SemanticsNode node) {
      labelToNodeIdAfterRebuild[node.label] = node.id;
      return true;
    });

    // Node IDs are stable.
    expect(nodeAfterRebuild.id, node.id);
    expect(labelToNodeIdAfterRebuild['Hallo '], labelToNodeId['Hallo ']);
    expect(labelToNodeIdAfterRebuild['Welt '], labelToNodeId['Welt ']);
    expect(labelToNodeIdAfterRebuild['!!!'], labelToNodeId['!!!']);
    expect(labelToNodeIdAfterRebuild.length, 3);

    final TapGestureRecognizer recognizer4 = TapGestureRecognizer();
    addTearDown(recognizer4.dispose);
    final TapGestureRecognizer recognizer5 = TapGestureRecognizer();
    addTearDown(recognizer5.dispose);

    // Remove one node.
    await tester.pumpWidget(Directionality(
      textDirection: TextDirection.ltr,
      child: Text.rich(
        TextSpan(
          text: 'Hallo ',
          recognizer: recognizer4..onTap = () {},
          children: <TextSpan>[
            TextSpan(
              text: 'Welt ',
              recognizer: recognizer5..onTap = () {},
            ),
          ],
        ),
      ),
    ));

    final SemanticsNode nodeAfterRemoval = tester.getSemantics(find.text('Hallo Welt '));
    final Map<String, int> labelToNodeIdAfterRemoval = <String, int>{};
    nodeAfterRemoval.visitChildren((SemanticsNode node) {
      labelToNodeIdAfterRemoval[node.label] = node.id;
      return true;
    });

    // Node IDs are stable.
    expect(nodeAfterRemoval.id, node.id);
    expect(labelToNodeIdAfterRemoval['Hallo '], labelToNodeId['Hallo ']);
    expect(labelToNodeIdAfterRemoval['Welt '], labelToNodeId['Welt ']);
    expect(labelToNodeIdAfterRemoval.length, 2);

    final TapGestureRecognizer recognizer6 = TapGestureRecognizer();
    addTearDown(recognizer6.dispose);
    final TapGestureRecognizer recognizer7 = TapGestureRecognizer();
    addTearDown(recognizer7.dispose);
    final TapGestureRecognizer recognizer8 = TapGestureRecognizer();
    addTearDown(recognizer8.dispose);

    await tester.pumpWidget(Directionality(
      textDirection: TextDirection.ltr,
      child: Text.rich(
        TextSpan(
          text: 'Hallo ',
          recognizer: recognizer6..onTap = () {},
          children: <TextSpan>[
            TextSpan(
              text: 'Welt ',
              recognizer: recognizer7..onTap = () {},
            ),
            TextSpan(
              text: '!!!',
              recognizer: recognizer8..onTap = () {},
            ),
          ],
        ),
      ),
    ));
    expect(find.text('Hallo Welt !!!'), findsOneWidget);
    final SemanticsNode nodeAfterAddition = tester.getSemantics(find.text('Hallo Welt !!!'));
    final Map<String, int> labelToNodeIdAfterAddition = <String, int>{};
    nodeAfterAddition.visitChildren((SemanticsNode node) {
      labelToNodeIdAfterAddition[node.label] = node.id;
      return true;
    });

    // New node gets a new ID.
    expect(nodeAfterAddition.id, node.id);
    expect(labelToNodeIdAfterAddition['Hallo '], labelToNodeId['Hallo ']);
    expect(labelToNodeIdAfterAddition['Welt '], labelToNodeId['Welt ']);
    expect(labelToNodeIdAfterAddition['!!!'], isNot(labelToNodeId['!!!']));
    expect(labelToNodeIdAfterAddition['!!!'], isNotNull);
    expect(labelToNodeIdAfterAddition.length, 3);

    semantics.dispose();
  });
}
