/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.snippet.telecom;

import static android.Manifest.permission.MANAGE_OWN_CALLS;
import static android.Manifest.permission.READ_PHONE_NUMBERS;

import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import androidx.annotation.RequiresPermission;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.android.mobly.snippet.Snippet;
import com.google.android.mobly.snippet.rpc.Rpc;
import java.time.Duration;
import java.util.concurrent.TimeoutException;
import org.json.JSONException;
import org.json.JSONObject;

/** Snippet class for PhoneAccount and PhoneAccountHandle APIs. */
public class PhoneAccountSnippet implements Snippet {

  private static class PhoneAccountSnippetException extends Exception {
    PhoneAccountSnippetException(String msg) {
      super(msg);
    }
  }

  private static final Duration PHONE_ACCOUNT_REGISTER_TIMEOUT = Duration.ofSeconds(3);
  private static final String EMERGENCY_HANDLE_ID = "E";

  private PhoneAccountHandle phoneAccountHandle = null;
  private PhoneAccountHandle selfManagedPhoneAccountHandle = null;
  private PhoneAccountHandle selfManagedPhoneAccountHandle2 = null;

  private final Context context;
  private final TelecomManager telecomManager;

  public PhoneAccountSnippet() {
    context = InstrumentationRegistry.getInstrumentation().getContext();
    telecomManager = context.getSystemService(TelecomManager.class);
  }

  @Rpc(description = "Returns the address, component name, and id of the phone account.")
  public JSONObject getTestPhoneAccountInfo() throws JSONException, PhoneAccountSnippetException {
    if (phoneAccountHandle == null) {
      throw new PhoneAccountSnippetException("PhoneAccount is not initialized");
    }
    return PhoneAccountUtils.convertPhoneAccountToJsonObject(phoneAccountHandle);
  }

  @RequiresPermission(READ_PHONE_NUMBERS)
  @Rpc(description = "Returns true if the test phone account is enabled, false otherwise.")
  public boolean isTestPhoneAccountEnabled() {
    if (phoneAccountHandle == null) {
      return false;
    }
    PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
    return phoneAccount != null && phoneAccount.isEnabled();
  }

  @Rpc(description = "Registers the test phone account.")
  public void registerTestPhoneAccount() throws PhoneAccountSnippetException, TimeoutException {
    if (phoneAccountHandle != null) {
      throw new PhoneAccountSnippetException(
          "PhoneAccount is already registered, please unregister the existing PhoneAccount first"
              + " before registering a new one.");
    }
    phoneAccountHandle = PhoneAccountUtils.TEST_PHONE_ACCOUNT_HANDLE;
    telecomManager.registerPhoneAccount(PhoneAccountUtils.TEST_PHONE_ACCOUNT);
    WaitUtils.waitUntilTrue(
        /* supplier= */ () -> telecomManager.getPhoneAccount(phoneAccountHandle) != null,
        /* timeoutInMillis= */ PHONE_ACCOUNT_REGISTER_TIMEOUT.toMillis(),
        /* errorMessage= */ "Timed out waiting for phone account to be registered.");
  }

  @RequiresPermission(MANAGE_OWN_CALLS)
  @Rpc(description = "Registers the self-managed phone account.")
  public void registerSelfManagedAccount() throws PhoneAccountSnippetException, TimeoutException {
    if (selfManagedPhoneAccountHandle != null || selfManagedPhoneAccountHandle2 != null) {
      throw new PhoneAccountSnippetException(
          "PhoneAccount is already registered, please unregister the existing PhoneAccount first"
              + " before registering a new one.");
    }
    selfManagedPhoneAccountHandle = PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE;
    telecomManager.registerPhoneAccount(PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT);
    selfManagedPhoneAccountHandle2 = PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2;
    telecomManager.registerPhoneAccount(PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_2);
    WaitUtils.waitUntilTrue(
        /* supplier= */ () ->
            telecomManager.getPhoneAccount(selfManagedPhoneAccountHandle) != null
                && telecomManager.getPhoneAccount(selfManagedPhoneAccountHandle2) != null,
        /* timeoutInMillis= */ PHONE_ACCOUNT_REGISTER_TIMEOUT.toMillis(),
        /* errorMessage= */ "Timed out waiting for phone account to be registered.");
  }

  @Rpc(description = "Returns true if the self-managed phone account is enabled, false otherwise.")
  public boolean isSelfManagedTestAccountEnabled() {
    if (selfManagedPhoneAccountHandle == null) {
      return false;
    }
    PhoneAccount account = telecomManager.getPhoneAccount(selfManagedPhoneAccountHandle);
    return account != null
        && account.isEnabled()
        && account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED);
  }

  @Rpc(
      description =
          "Returns true if the default outgoing phone account for tel scheme is SIM based, false"
              + " otherwise.")
  public boolean isDefaultOutgoingAccountSimBased() {
    PhoneAccountHandle phoneAccountHandle =
        telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
    PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
    if (phoneAccount == null) {
      return false;
    }
    return phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
        && !(phoneAccountHandle.getId().equals(EMERGENCY_HANDLE_ID));
  }

  @Rpc(description = "Unregisters the phone account.")
  public void unregisterPhoneAccounts() {
    if (phoneAccountHandle != null) {
      telecomManager.unregisterPhoneAccount(phoneAccountHandle);
    }
    if (selfManagedPhoneAccountHandle != null) {
      telecomManager.unregisterPhoneAccount(selfManagedPhoneAccountHandle);
    }
    if (selfManagedPhoneAccountHandle2 != null) {
      telecomManager.unregisterPhoneAccount(selfManagedPhoneAccountHandle2);
    }
  }

  @Override
  public void shutdown() {
    unregisterPhoneAccounts();
  }
}

