import {
  by, device, element, expect, waitFor,
} from "detox";

import { iNatE2eAfterEach, iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
import closeOnboarding from "./sharedFlows/closeOnboarding";
import signIn from "./sharedFlows/signIn";
import uploadObservation from "./sharedFlows/uploadObservation";

const TIMEOUT = 10_000;

function delay( ms ) {
  return new Promise( resolve => { setTimeout( resolve, ms ); } );
}

describe( "Signed in user", () => {
  beforeAll( async ( ) => iNatE2eBeforeAll( device ) );
  beforeEach( async ( ) => iNatE2eBeforeEach( device ) );
  afterEach( async ( ) => iNatE2eAfterEach( device ) );

  async function createAndUploadObservation( options = { upload: false } ) {
    const addObsButton = element( by.id( "add-obs-button" ) );
    await waitFor( addObsButton ).toBeVisible().withTimeout( TIMEOUT );
    await addObsButton.tap();
    await expect( element( by.id( "observe-without-evidence-button" ) ) ).toBeVisible();
    // Observe without evidence
    const obsWithoutEvidenceButton = element(
      by.id( "observe-without-evidence-button" ),
    );
    await expect( obsWithoutEvidenceButton ).toBeVisible();
    await obsWithoutEvidenceButton.tap();

    await uploadObservation( options );

    const obsListItem = element( by.id( /MyObservations\.obsListItem\..*/ ) ).atIndex( 0 );
    const obsListItemAttributes = await obsListItem.getAttributes( );
    const uuid = obsListItemAttributes.elements
      ? obsListItemAttributes.elements[0].identifier.split( "." ).pop( )
      : obsListItemAttributes.identifier.split( "." ).pop( );

    const listItem = element( by.id( `MyObservations.obsListItem.${uuid}` ) );
    await waitFor( listItem ).toBeVisible().withTimeout( TIMEOUT );

    return uuid;
  }

  async function deleteObservationByUUID( uuid, username ) {
    const obsListItem = element( by.id( `MyObservations.obsListItem.${uuid}` ) );
    await obsListItem.tap();

    const editButton = element( by.id( "ObsDetail.editButton" ) );
    await waitFor( editButton ).toBeVisible().withTimeout( TIMEOUT );
    // Navigate to the edit screen
    await editButton.tap();

    // Check that the edit screen is visible
    await waitFor( element( by.text( "EVIDENCE" ) ) )
      .toBeVisible()
      .withTimeout( TIMEOUT );
    // Press header kebab menu
    const headerKebabMenu = element( by.id( "KebabMenu.Button" ) );
    await expect( headerKebabMenu ).toBeVisible();
    await headerKebabMenu.tap();
    // Press delete observation
    const deleteObservationMenuItem = element( by.id( "Header.delete-observation" ) );
    await waitFor( deleteObservationMenuItem ).toBeVisible().withTimeout( TIMEOUT );
    await deleteObservationMenuItem.tap();
    // Check that the delete button is visible
    const deleteObservationButton = element( by.text( "DELETE" ) );
    await waitFor( deleteObservationButton ).toBeVisible().withTimeout( TIMEOUT );
    // Press delete observation
    await deleteObservationButton.tap();

    // Make sure we're back on MyObservations
    await waitFor( username ).toBeVisible().withTimeout( TIMEOUT );
  }

  it( "should create an observation, add a comment, and delete the observation", async () => {
    await closeOnboarding( );
    /*
    / 1. Sign in
    */
    const username = await signIn( );

    // Switch to list view as well
    const listToggle = element( by.id( "SegmentedButton.list" ) );
    await waitFor( listToggle ).toBeVisible( ).withTimeout( TIMEOUT );
    await listToggle.tap();

    /*
    / 2. Create two observations without evidence
    */
    const uuid = await createAndUploadObservation( { upload: true } );
    // Create a second b/c later we want to test that the deleted status text
    // is visible in the toolbar, and the toolbar will disappear if there are
    // zero observations
    await createAndUploadObservation( );

    /*
    / 3. Update the observation by adding a comment
    */
    const obsListItem = element( by.id( `MyObservations.obsListItem.${uuid}` ) );
    await obsListItem.tap();
    const commentButton = element( by.id( "ObsDetail.commentButton" ) );
    await waitFor( commentButton ).toBeVisible().withTimeout( TIMEOUT );
    await commentButton.tap();
    // Check that the comment modal is visible
    const commentModalInput = element( by.id( "TextInputSheet.notes" ) );
    await waitFor( commentModalInput ).toBeVisible().withTimeout( TIMEOUT );
    // Add a comment
    await commentModalInput.tap();
    await commentModalInput.typeText( "This is a comment" );
    // Tap on the title of the modal to dismiss the keyboard
    const bottomSheetHeader = element( by.id( "bottom-sheet-header" ) );
    await bottomSheetHeader.tap();
    // Tap the submit button
    const commentModalSubmitButton = element( by.id( "TextInputSheet.confirm" ) );
    await commentModalSubmitButton.tap();
    // Check that the comment is visible
    await element( by.id( `ObsDetails.${uuid}` ) ).scrollTo( "bottom" );
    const comment = element( by.text( "This is a comment" ) );
    await waitFor( comment ).toBeVisible().withTimeout( TIMEOUT );
    await element( by.id( "header-back-button" ) ).tap( );
    await waitFor( username ).toBeVisible( ).withTimeout( TIMEOUT );

    /*
    / 4. Delete the two observations without evidence
    */
    await deleteObservationByUUID( uuid, username );
    // It would be nice to test for the "1 observation deleted" status text in
    // the toolbar, but that message appears ephemerally and sometimes this
    // test doesn't pick it up on the Github runner. Since we created two
    // observations, the upload prompt will be the stable status text at this
    // point, and we can confirm deletion by testing for the absence of the
    // list item for the observation we deleted.
    await waitFor( element( by.text( /Upload 1 observation/ ) ) ).toBeVisible( ).withTimeout( 20_000 );

    // the timing of syncing deletions seems to be different in the actual app versus these
    // e2e tests, so deleting an observation here still shows the observation
    // in the list unless this delay( ) is added
    await delay( 10000 );
    await expect( obsListItem ).not.toBeVisible( );
  } );
} );
