import { exec, execSync } from "child_process";

import resetUserForTesting from "./sharedFlows/resetUserForTesting";

function execPromise( command ) {
  return new Promise( ( resolve, reject ) => {
    exec( command, ( error, stdout, stderr ) => {
      if ( error ) {
        console.log( `Error executing command: ${command}` );
        console.log( `stderr: ${stderr}` );
        reject( error );
        return;
      }
      resolve( stdout );
    } );
  } );
}

export async function iNatE2eBeforeAll( device ) {
  await resetUserForTesting();

  if ( device.getPlatform( ) === "android" ) {
    // Push a test image into the app's external files directory so the mock
    // camera can use it as a photo source (copyAssetsFileIOS is iOS-only).
    // The directory is created by the app on first launch, so we ensure it
    // exists before pushing.
    await execPromise(
      "adb shell mkdir -p /sdcard/Android/data/org.inaturalist.iNaturalistMobile/files/",
    );
    await execPromise(
      "adb push e2e/animal.jpg"
        + " /sdcard/Android/data/org.inaturalist.iNaturalistMobile/files/e2e_test.jpg",
    );
  }
}

export async function iNatE2eBeforeEach( device ) {
  const launchAppOptions = {
    newInstance: true,
    permissions: {
      location: "always",
      camera: "YES",
      medialibrary: "YES",
      photos: "YES",
    },
  };
  try {
    await device.launchApp( launchAppOptions );
  } catch ( launchAppError ) {
    if ( !launchAppError.message.match( /unexpectedly disconnected/ ) ) {
      throw launchAppError;
    }
    // Try it one more time
    await device.launchApp( launchAppOptions );
  }
  if ( device.getPlatform( ) === "ios" ) {
    // disable password autofill
    execSync(
      // eslint-disable-next-line max-len
      `plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/UserSettings.plist`,
    );
    execSync(
      // eslint-disable-next-line max-len
      `plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Library/UserConfigurationProfiles/EffectiveUserSettings.plist`,
    );
    execSync(
      // eslint-disable-next-line max-len
      `plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Library/UserConfigurationProfiles/PublicInfo/PublicEffectiveUserSettings.plist`,
    );
  }
}

async function getSimulatorId() {
  try {
    // List all available simulators
    const output = await execPromise( "xcrun simctl list devices --json" );
    const { devices } = JSON.parse( output );

    // Use Object.values and Array.find instead of loops
    const bootedDevice = Object.entries( devices )
      .flatMap( ( [_runtime, deviceList] ) => deviceList ) // Use _ prefix for unused variables
      .find( device => device.state === "Booted" );

    if ( bootedDevice ) {
      console.log( `Found booted simulator: ${bootedDevice.name} (${bootedDevice.udid})` );
      return bootedDevice.udid;
    }

    console.log( "No booted simulator found" );
    return null;
  } catch ( error ) {
    console.log( "Error getting simulator ID:", error.message );
    return null;
  }
}

export function terminateApp( deviceId, bundleId ) {
  try {
    console.log( `Attempting to terminate ${bundleId} on device ${deviceId}...` );
    const result = execSync( `/usr/bin/xcrun simctl terminate ${deviceId} ${bundleId}` );
    console.log( "App terminated successfully", result.toString() );
    return true;
  } catch ( error ) {
    if ( error.stderr && error.stderr.toString().includes( "found nothing to terminate" ) ) {
      console.log( "App is not running, nothing to terminate." );
      return true;
    }
    console.error( "Error during app termination:", error.message );
    return false;
  }
}

export async function iNatE2eAfterEach( device ) {
  await resetUserForTesting();

  if ( device && device.getPlatform() === "android" ) {
    return;
  }

  try {
    // Try to use device.terminateApp first (the built-in Detox method)
    if ( device ) {
      try {
        await device.terminateApp();
        console.log( "App terminated through Detox" );
        // Add a small delay to let Detox processes settle
        await new Promise( resolve => { setTimeout( resolve, 300 ); } );
        return;
      } catch ( detoxError ) {
        console.log(
          "Detox terminateApp failed, falling back to manual termination:",
          detoxError.message,
        );
      }
    }

    // Fall back to manual termination if Detox method fails or device is unavailable
    const deviceId = await getSimulatorId();
    const bundleId = "org.inaturalist.iNaturalistMobile";

    if ( deviceId && bundleId ) {
      console.log( "Using manual termination via simctl" );
      // Use existing terminateApp, but don't throw errors
      try {
        await terminateApp( deviceId, bundleId );
      } catch ( error ) {
        console.log( "Manual termination error (non-fatal):", error.message );
      }

      // Add a delay to let processes settle
      await new Promise( resolve => { setTimeout( resolve, 500 ); } );
    }
  } catch ( error ) {
    console.log( "Error during cleanup (non-fatal):", error.message );
  }
}
