/*
 * Copyright (C) 2022 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.android.server.telecom.voip;

import static android.Manifest.permission.CALL_PRIVILEGED;
import static android.telecom.CallAttributes.CALL_CAPABILITIES_KEY;
import static android.telecom.CallAttributes.DISPLAY_NAME_KEY;
import static android.telecom.CallException.CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME;

import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.telecom.CallAttributes;
import android.telecom.TelecomManager;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.LoggedHandlerExecutor;
import com.android.server.telecom.flags.FeatureFlags;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

public class OutgoingCallTransaction extends VoipCallTransaction {

    private static final String TAG = OutgoingCallTransaction.class.getSimpleName();
    private final String mCallId;
    private final Context mContext;
    private final String mCallingPackage;
    private final CallAttributes mCallAttributes;
    private final CallsManager mCallsManager;
    private final Bundle mExtras;
    private FeatureFlags mFeatureFlags;

    public void setFeatureFlags(FeatureFlags featureFlags) {
        mFeatureFlags = featureFlags;
    }

    public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes,
            CallsManager callsManager, Bundle extras, FeatureFlags featureFlags) {
        super(callsManager.getLock());
        mCallId = callId;
        mContext = context;
        mCallAttributes = callAttributes;
        mCallsManager = callsManager;
        mExtras = extras;
        mCallingPackage = mContext.getOpPackageName();
        mFeatureFlags = featureFlags;
    }

    public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes,
            CallsManager callsManager, FeatureFlags featureFlags) {
        this(callId, context, callAttributes, callsManager, new Bundle(), featureFlags);
    }

    @Override
    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
        Log.d(TAG, "processTransaction");

        final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
                CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;

        final Intent intent = new Intent(hasCallPrivilegedPermission ?
                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, mCallAttributes.getAddress());

        if (mCallsManager.isOutgoingCallPermitted(mCallAttributes.getPhoneAccountHandle())) {
            Log.d(TAG, "processTransaction: outgoing call permitted");

            CompletableFuture<Call> callFuture =
                    mCallsManager.startOutgoingCall(mCallAttributes.getAddress(),
                            mCallAttributes.getPhoneAccountHandle(),
                            generateExtras(mCallAttributes),
                            mCallAttributes.getPhoneAccountHandle().getUserHandle(),
                            intent,
                            mCallingPackage);

            if (callFuture == null) {
                return CompletableFuture.completedFuture(
                        new VoipCallTransactionResult(
                                CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                                "incoming call not permitted at the current time"));
            }
            CompletionStage<VoipCallTransactionResult> result = callFuture.thenComposeAsync(
                    (call) -> {

                        Log.d(TAG, "processTransaction: completing future");

                        if (call == null) {
                            Log.d(TAG, "processTransaction: call is null");
                            return CompletableFuture.completedFuture(
                                    new VoipCallTransactionResult(
                                            CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                                            "call could not be created at this time"));
                        } else {
                            Log.d(TAG, "processTransaction: call done. id=" + call.getId());
                        }

                        return CompletableFuture.completedFuture(
                                new VoipCallTransactionResult(
                                        VoipCallTransactionResult.RESULT_SUCCEED,
                                        call, null));
                    }
                    , new LoggedHandlerExecutor(mHandler, "OCT.pT", null));

            return result;
        } else {
            return CompletableFuture.completedFuture(
                    new VoipCallTransactionResult(
                            CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                            "incoming call not permitted at the current time"));

        }
    }

    @VisibleForTesting
    public Bundle generateExtras(CallAttributes callAttributes) {
        mExtras.setDefusable(true);
        mExtras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
        mExtras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
        if (mFeatureFlags.transactionalVideoState()) {
            // Transactional calls need to remap the CallAttributes video state to the existing
            // VideoProfile for consistency.
            mExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    TransactionalVideoStateToVideoProfileState(callAttributes.getCallType()));
        } else {
            mExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    callAttributes.getCallType());
        }
        mExtras.putCharSequence(DISPLAY_NAME_KEY, callAttributes.getDisplayName());
        return mExtras;
    }
}