// In this case we *do* want manual cleanup because we want to make sure the
// component tree has been taken down before we delete all the content in
// Realm. Otherwise we get after effects from listeners on Realm, Realm
// queries, and Realm objects that are hard/impossible to wait for before
// tests finish
// eslint-disable-next-line testing-library/no-manual-cleanup
import { cleanup } from "@testing-library/react-native";
import { API_HOST, clearAuthCache } from "components/LoginSignUp/AuthenticationService";
import { getInatLocaleFromSystemLocale } from "i18n/initI18next";
import i18next from "i18next";
import inatjs from "inaturalistjs";
import nock from "nock";
import RNSInfo from "react-native-sensitive-info";
import changeLanguage from "sharedHelpers/changeLanguage";
import safeRealmWrite from "sharedHelpers/safeRealmWrite";
import { makeResponse } from "tests/factory";

const TEST_JWT = "test-json-web-token";
const TEST_ACCESS_TOKEN = "test-access-token";
const rnsiOptions = {
  keychainService: "app",
};

async function signOut( options = {} ) {
  const realm = options.realm || global.realm;
  i18next.language = undefined;
  // As mentioned above, clean up the component tree so we don't have after
  // effects related to deleting all realm objects.
  // Note: if we ever write tests explicitly testing UI sign out, we might
  // want to write an optional exception to this step
  cleanup( );
  // This is the nuclear option, maybe revisit if it's a source of bugs
  safeRealmWrite( realm, ( ) => {
    realm.deleteAll( );
  }, "deleting entire realm in signOut function, user.js" );
  const systemLocale = getInatLocaleFromSystemLocale( );
  changeLanguage( systemLocale );
  await RNSInfo.deleteItem( "username", rnsiOptions );
  await RNSInfo.deleteItem( "jwtToken", rnsiOptions );
  await RNSInfo.deleteItem( "jwtGeneratedAt", rnsiOptions );
  await RNSInfo.deleteItem( "accessToken", rnsiOptions );
  clearAuthCache( );
  inatjs.users.me.mockClear( );
}

async function signIn( user, options = {} ) {
  const realm = options.realm || global.realm;
  await RNSInfo.setItem( "username", user.login, rnsiOptions );
  await RNSInfo.setItem( "jwtToken", TEST_JWT, rnsiOptions );
  await RNSInfo.setItem( "jwtGeneratedAt", Date.now( ).toString( ), rnsiOptions );
  await RNSInfo.setItem( "accessToken", TEST_ACCESS_TOKEN, rnsiOptions );
  clearAuthCache( );
  inatjs.users.me.mockResolvedValue( makeResponse( [user] ) );
  user.signedIn = true;
  safeRealmWrite( realm, ( ) => {
    realm.create( "User", user, "modified" );
  }, "signing user in, user.js" );
  if ( user?.locale ) {
    changeLanguage( user.locale );
  }
  nock( API_HOST )
    .post( "/oauth/token" )
    .reply( 200, { access_token: TEST_ACCESS_TOKEN } )
    .get( "/users/edit.json" )
    .reply( 200, { login: user.login, id: user.id } );
  nock( API_HOST, {
    reqheaders: {
      authorization: `Bearer ${TEST_ACCESS_TOKEN}`,
    },
  } )
    .get( "/users/api_token.json" )
    .reply( 200, { api_token: "some-jwt" } );
}

export {
  signIn,
  signOut,
  TEST_ACCESS_TOKEN,
  TEST_JWT,
};
