content: import 'package:app/pages/journey/train_journey/widgets/table/combined_foot_note_operational_indication_row.dart';
  import 'package:app/pages/journey/train_journey/widgets/table/uncoded_operational_indication_accordion.dart';
  import 'package:app/widgets/accordion/accordion.dart';
  import 'package:app/widgets/table/das_table.dart';
  import 'package:app/widgets/table/scrollable_align.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter_test/flutter_test.dart';
  import 'package:sfera/component.dart';
  
  import '../app_test.dart';
  import '../util/test_utils.dart';
  
  void main() {
    testWidgets('test operational indication collapsible', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T22M');
  
      final dataToTest = UncodedOperationalIndication(order: 0, texts: ['Renens VD: Halt an Halteort 3']);
      final identifier = dataToTest.hashCode;
      await _checkCollapsible(identifier, tester);
  
      await disconnect(tester);
    });
    testWidgets('test RADN foot note collapsible', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T15M');
  
      final footnote = FootNote(
        type: FootNoteType.decisiveGradientDown,
        refText: '1)',
        text: 'Renens - Lausanne <i>"via saut-de-mouton"</i> 0‰',
      );
      final opFootNote = OpFootNote(order: 0, footNote: footnote);
      final identifier = opFootNote.hashCode;
      await _checkCollapsible(identifier, tester);
  
      await disconnect(tester);
    });
    testWidgets('test show more on long texts of operational indications', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T22M');
  
      final textToSearch = 'Pully: Vorziehen bis Ende Perron.';
  
      // scroll to testable row
      await dragUntilTextInStickyHeader(tester, 'Pully');
  
      // should not be collapsed by default
      final accordion = _findDASTableAccordionByContainsText(textToSearch, UncodedOperationalIndicationAccordion);
      _checkCollapsibleRow(isCollapsed: false, collapsibleRow: accordion);
  
      // should have show more button and collapsed content
      final collapsedContent = find.descendant(
        of: accordion,
        matching: find.byKey(UncodedOperationalIndicationAccordion.collapsedContentKey),
      );
      expect(collapsedContent, findsOneWidget);
      var showMoreButton = find.descendant(
        of: accordion,
        matching: find.byKey(UncodedOperationalIndicationAccordion.showMoreTextKey),
      );
      expect(showMoreButton, findsOneWidget);
  
      // should show full text after tap on row and no button
      await tapElement(tester, showMoreButton);
      await tester.pumpAndSettle(ScrollableAlign.alignScrollDuration);
  
      final rowWithExpandedText = _findDASTableAccordionByContainsText(
        'Lorem ipsum dolor sit amet, consetetur sadipscing elitr',
        UncodedOperationalIndicationAccordion,
      );
      expect(rowWithExpandedText, findsOneWidget);
      showMoreButton = find.descendant(
        of: rowWithExpandedText,
        matching: find.byKey(UncodedOperationalIndicationAccordion.showMoreTextKey),
      );
      expect(showMoreButton, findsNothing);
  
      await disconnect(tester);
    });
    testWidgets('test combined operational indications and replaced new lines with " ;"', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T22M');
  
      // scroll to testable row
      await dragUntilTextInStickyHeader(tester, 'Lausanne');
  
      // should have show more button and collapsed content with " ;" delimiter
      final accordion = _findDASTableAccordionByContainsText(
        'Strecke INN - MR: Bahnübergangsanlagen ohne Balisenüberwachung; Straba. = Strassenbahnbereich;',
        UncodedOperationalIndicationAccordion,
      );
      final collapsedContent = find.descendant(
        of: accordion,
        matching: find.byKey(UncodedOperationalIndicationAccordion.collapsedContentKey),
      );
      expect(collapsedContent, findsOneWidget);
      final showMoreButton = find.descendant(
        of: accordion,
        matching: find.byKey(UncodedOperationalIndicationAccordion.showMoreTextKey),
      );
      expect(showMoreButton, findsOneWidget);
  
      // should show full content without " ;" after tap on show more
      await tapElement(tester, accordion);
      final expandedRow = _findDASTableAccordionByContainsText(
        'Strecke INN - MR: Bahnübergangsanlagen ohne Balisenüberwachung\nStraba. = Strassenbahnbereich',
        UncodedOperationalIndicationAccordion,
      );
      expect(expandedRow, findsOneWidget);
  
      // should show text of combined operational indication
      final combinedText = find.descendant(
        of: expandedRow,
        matching: find.textContaining('Lausanne: Halt an Halteort 2'),
      );
      expect(combinedText, findsOneWidget);
  
      await disconnect(tester);
    });
    testWidgets('test row combined for operational indication and foot note on same service point', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T22M');
  
      final scrollableFinder = find.byType(AnimatedList);
      await tester.dragUntilVisible(
        find.byKey(CombinedFootNoteOperationalIndicationRow.rowKey),
        scrollableFinder,
        const Offset(0, -100),
      );
  
      final combinedRow = find.byKey(CombinedFootNoteOperationalIndicationRow.rowKey);
      expect(combinedRow, findsOneWidget);
  
      final operationalIndicationRow = find.descendant(
        of: combinedRow,
        matching: find.textContaining(l10n.c_uncoded_operational_indication),
      );
      expect(operationalIndicationRow, findsOneWidget);
  
      final footNoteRow = find.descendant(
        of: combinedRow,
        matching: find.textContaining(l10n.c_radn),
      );
      expect(footNoteRow, findsOneWidget);
  
      await disconnect(tester);
    });
    testWidgets('test operational indication collapsed when passed', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T22');
  
      final dataToTest = UncodedOperationalIndication(order: 0, texts: ['Renens VD: Halt an Halteort 3']);
      final identifier = dataToTest.hashCode;
      await _checkCollapsedWhenPassed(identifier, tester);
  
      await disconnect(tester);
    });
    testWidgets('test RADN foot notes collapsed when passed', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T15');
  
      final footnote = FootNote(
        type: FootNoteType.decisiveGradientDown,
        refText: '1)',
        text: 'Renens - Lausanne <i>"via saut-de-mouton"</i> 0‰',
      );
      final opFootNote = OpFootNote(order: 0, footNote: footnote);
      final identifier = opFootNote.hashCode;
  
      await _checkCollapsedWhenPassed(identifier, tester);
  
      await disconnect(tester);
    });
  
    testWidgets('test RADN foot notes title contain type', (tester) async {
      await prepareAndStartApp(tester);
      await loadTrainJourney(tester, trainNumber: 'T15M');
  
      expect(find.textContaining(l10n.c_radn_type_decisive_gradient_down), findsOneWidget);
      expect(find.textContaining(l10n.c_radn_type_journey), findsAny);
  
      await disconnect(tester);
    });
  }
  
  Future<void> _checkCollapsible(int identifier, WidgetTester tester) async {
    // should be expanded by default
    _checkCollapsibleRow(identifier: identifier, isCollapsed: false);
  
    // should be collapsed after tap
    await tapElement(tester, _findDASTableAccordionRowByKey(identifier), warnIfMissed: false);
    _checkCollapsibleRow(identifier: identifier, isCollapsed: true);
  
    // should be reopened after tap
    await tapElement(tester, _findDASTableAccordionRowByKey(identifier), warnIfMissed: false);
    _checkCollapsibleRow(identifier: identifier, isCollapsed: false);
  }
  
  Future<void> _checkCollapsedWhenPassed(int identifier, WidgetTester tester) async {
    // should be expanded by default
    _checkCollapsibleRow(identifier: identifier, isCollapsed: false);
  
    // wait for positional update and scroll back up
    await tester.pumpAndSettle(Duration(seconds: 2));
    await tester.drag(find.byType(AnimatedList), const Offset(0, 300));
  
    _checkCollapsibleRow(identifier: identifier, isCollapsed: true);
  }
  
  void _checkCollapsibleRow({required bool isCollapsed, Object? identifier, Finder? collapsibleRow}) {
    final rowToTest = collapsibleRow ?? _findDASTableAccordionRowByKey(identifier!);
  
    final accordions = find.descendant(
      of: rowToTest,
      matching: find.byKey(isCollapsed ? Accordion.collapsedKey : Accordion.expandedKey),
    );
  
    expect(accordions, findsAny);
  }
  
  Finder _findDASTableAccordionRowByKey(Object identifier) {
    return find.descendant(
      of: find.byKey(DASTable.tableKey),
      matching: find.ancestor(of: find.byKey(ObjectKey(identifier)), matching: find.byKey(DASTable.rowKey)),
    );
  }
  
  Finder _findDASTableAccordionByContainsText(String text, Type accordion) {
    return find.descendant(
      of: find.byKey(DASTable.tableKey),
      matching: find.ancestor(of: find.textContaining(text, findRichText: true), matching: find.byType(accordion)),
    );
  }
  