/*
 * Copyright 2023 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.
 */

#include "security_event_parser.h"

#include <bluetooth/log.h>

#include <optional>
#include <string>

#include "btm_sec.h"
#include "common/metrics.h"
#include "hci/hci_packets.h"
#include "main/shim/helpers.h"
#include "stack/include/btm_sec_api_types.h"
#include "stack/include/hci_error_code.h"
#include "stack/include/sec_hci_link_interface.h"
#include "stack/include/stack_metrics_logging.h"
#include "types/raw_address.h"

using namespace bluetooth;
using namespace bluetooth::hci;
using android::bluetooth::hci::CMD_UNKNOWN;
using android::bluetooth::hci::STATUS_UNKNOWN;
using bluetooth::common::kUnknownConnectionHandle;

namespace bluetooth::stack::btm {
namespace {
static void log_address_and_status(const Address& bda, EventCode event_code,
                                   ErrorCode event_status) {
  uint32_t cmd = android::bluetooth::hci::CMD_UNKNOWN;
  uint16_t status = static_cast<uint16_t>(event_status);
  uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN;
  uint16_t handle = bluetooth::common::kUnknownConnectionHandle;
  int64_t value = 0;
  log_classic_pairing_event(ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code),
                            status, reason, value);
}
static void log_address(const Address& bda, EventCode event_code) {
  uint32_t cmd = android::bluetooth::hci::CMD_UNKNOWN;
  uint16_t status = android::bluetooth::hci::STATUS_UNKNOWN;
  uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN;
  uint16_t handle = bluetooth::common::kUnknownConnectionHandle;
  int64_t value = 0;
  log_classic_pairing_event(ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code),
                            status, reason, value);
}
static void parse_encryption_change(const EventView event) {
  auto change_opt = EncryptionChangeView::CreateOptional(event);
  log::assert_that(change_opt.has_value(), "assert failed: change_opt.has_value()");
  auto change = change_opt.value();

  ErrorCode status = change.GetStatus();
  uint16_t handle = change.GetConnectionHandle();
  EncryptionEnabled encr_enable = change.GetEncryptionEnabled();

  btm_sec_encryption_change_evt(handle, static_cast<tHCI_STATUS>(status),
                                static_cast<uint8_t>(encr_enable), 0);
  log_classic_pairing_event(
          ToRawAddress(Address::kEmpty), handle, android::bluetooth::hci::CMD_UNKNOWN,
          static_cast<uint32_t>(change.GetEventCode()), static_cast<uint16_t>(status),
          android::bluetooth::hci::STATUS_UNKNOWN, 0);
}
static void parse_encryption_change_v2(const EventView event) {
  auto change_opt = EncryptionChangeV2View::CreateOptional(event);
  log::assert_that(change_opt.has_value(), "assert failed: change_opt.has_value()");
  auto change = change_opt.value();

  ErrorCode status = change.GetStatus();
  uint16_t handle = change.GetConnectionHandle();
  EncryptionEnabled encr_enable = change.GetEncryptionEnabled();
  uint8_t key_size = change.GetKeySize();

  btm_sec_encryption_change_evt(handle, static_cast<tHCI_STATUS>(status),
                                static_cast<uint8_t>(encr_enable), key_size);
  log_classic_pairing_event(
          ToRawAddress(Address::kEmpty), handle, android::bluetooth::hci::CMD_UNKNOWN,
          static_cast<uint32_t>(change.GetEventCode()), static_cast<uint16_t>(status),
          android::bluetooth::hci::STATUS_UNKNOWN, 0);
}
static void parse_change_connection_link_key_complete(const EventView event) {
  auto complete_opt = ChangeConnectionLinkKeyCompleteView::CreateOptional(event);
  log::assert_that(complete_opt.has_value(), "assert failed: complete_opt.has_value()");
  auto complete = complete_opt.value();

  log_classic_pairing_event(
          ToRawAddress(Address::kEmpty), complete.GetConnectionHandle(),
          android::bluetooth::hci::CMD_UNKNOWN, static_cast<uint32_t>(complete.GetEventCode()),
          static_cast<uint16_t>(complete.GetStatus()), android::bluetooth::hci::STATUS_UNKNOWN, 0);
}
static void parse_central_link_key_complete(const EventView event) {
  auto event_opt = CentralLinkKeyCompleteView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto complete = event_opt.value();

  log::info("Unhandled event: {}", EventCodeText(event.GetEventCode()));
}
static void parse_return_link_keys(const EventView event) {
  auto event_opt = ReturnLinkKeysView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto view = event_opt.value();

  log::info("Unhandled event: {}", EventCodeText(event.GetEventCode()));
}
static void parse_pin_code_request(const EventView event) {
  auto event_opt = PinCodeRequestView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto request = event_opt.value();
  btm_sec_pin_code_request(ToRawAddress(request.GetBdAddr()));
}
static void parse_link_key_request(const EventView event) {
  auto event_opt = LinkKeyRequestView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto request = event_opt.value();

  btm_sec_link_key_request(ToRawAddress(request.GetBdAddr()));
  log_address(request.GetBdAddr(), event.GetEventCode());
}
static void parse_link_key_notification(const EventView event) {
  auto event_opt = LinkKeyNotificationView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto notification = event_opt.value();

  btm_sec_link_key_notification(ToRawAddress(notification.GetBdAddr()), notification.GetLinkKey(),
                                static_cast<uint8_t>(notification.GetKeyType()));
  log_address(notification.GetBdAddr(), event.GetEventCode());
}
static void parse_encryption_key_refresh_complete(const EventView event) {
  auto event_opt = EncryptionKeyRefreshCompleteView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto refresh = event_opt.value();

  btm_sec_encryption_key_refresh_complete(refresh.GetConnectionHandle(),
                                          static_cast<tHCI_STATUS>(refresh.GetStatus()));
}
static void parse_io_capabilities_req(const EventView event) {
  auto event_opt = IoCapabilityRequestView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto request = event_opt.value();

  RawAddress peer = ToRawAddress(request.GetBdAddr());

  btm_io_capabilities_req(peer);
  log_address(request.GetBdAddr(), event.GetEventCode());
}
static void parse_io_capabilities_rsp(const EventView event) {
  auto response_opt = IoCapabilityResponseView::CreateOptional(event);
  log::assert_that(response_opt.has_value(), "assert failed: response_opt.has_value()");
  auto response = response_opt.value();

  tBTM_SP_IO_RSP evt_data{
          .bd_addr = ToRawAddress(response.GetBdAddr()),
          .io_cap = static_cast<tBTM_IO_CAP>(response.GetIoCapability()),
          .oob_data = static_cast<tBTM_OOB_DATA>(response.GetOobDataPresent()),
          .auth_req = static_cast<tBTM_AUTH_REQ>(response.GetAuthenticationRequirements()),
  };

  btm_io_capabilities_rsp(evt_data);
  log_address(response.GetBdAddr(), event.GetEventCode());
}
static void parse_remote_oob_data_request(const EventView event) {
  auto event_opt = RemoteOobDataRequestView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto request = event_opt.value();

  btm_rem_oob_req(ToRawAddress(request.GetBdAddr()));
  log_address(request.GetBdAddr(), event.GetEventCode());
}
static void parse_simple_pairing_complete(const EventView event) {
  auto event_opt = SimplePairingCompleteView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto complete = event_opt.value();

  btm_simple_pair_complete(ToRawAddress(complete.GetBdAddr()),
                           static_cast<uint8_t>(complete.GetStatus()));
  log_address_and_status(complete.GetBdAddr(), event.GetEventCode(), complete.GetStatus());
}
static void parse_user_passkey_notification(const EventView event) {
  auto event_opt = UserPasskeyNotificationView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto notification = event_opt.value();

  btm_proc_sp_req_evt(BTM_SP_KEY_NOTIF_EVT, ToRawAddress(notification.GetBdAddr()),
                      notification.GetPasskey());
  log_address(notification.GetBdAddr(), event.GetEventCode());
}
static void parse_keypress_notification(const EventView event) {
  auto event_opt = KeypressNotificationView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto notification = event_opt.value();

  log::info("Unhandled event: {}", EventCodeText(event.GetEventCode()));
  log_address(notification.GetBdAddr(), event.GetEventCode());
}
static void parse_user_confirmation_request(const EventView event) {
  auto event_opt = UserConfirmationRequestView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto request = event_opt.value();

  btm_proc_sp_req_evt(BTM_SP_CFM_REQ_EVT, ToRawAddress(request.GetBdAddr()),
                      request.GetNumericValue());
  log_address(request.GetBdAddr(), event.GetEventCode());
}
static void parse_user_passkey_request(const EventView event) {
  auto event_opt = UserPasskeyRequestView::CreateOptional(event);
  log::assert_that(event_opt.has_value(), "assert failed: event_opt.has_value()");
  auto request = event_opt.value();

  btm_proc_sp_req_evt(BTM_SP_KEY_REQ_EVT, ToRawAddress(request.GetBdAddr()),
                      0 /* No value needed */);
  log_address(request.GetBdAddr(), event.GetEventCode());
}
}  // namespace
}  // namespace bluetooth::stack::btm

namespace bluetooth::stack::btm {

void SecurityEventParser::OnSecurityEvent(bluetooth::hci::EventView event) {
  switch (event.GetEventCode()) {
    case EventCode::ENCRYPTION_CHANGE:
      parse_encryption_change(event);
      break;
    case EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE:
      parse_change_connection_link_key_complete(event);
      break;
    case EventCode::CENTRAL_LINK_KEY_COMPLETE:
      parse_central_link_key_complete(event);
      break;
    case EventCode::RETURN_LINK_KEYS:
      parse_return_link_keys(event);
      break;
    case EventCode::PIN_CODE_REQUEST:
      parse_pin_code_request(event);
      break;
    case EventCode::LINK_KEY_REQUEST:
      parse_link_key_request(event);
      break;
    case EventCode::LINK_KEY_NOTIFICATION:
      parse_link_key_notification(event);
      break;
    case EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE:
      parse_encryption_key_refresh_complete(event);
      break;
    case EventCode::IO_CAPABILITY_REQUEST:
      parse_io_capabilities_req(event);
      break;
    case EventCode::IO_CAPABILITY_RESPONSE:
      parse_io_capabilities_rsp(event);
      break;
    case EventCode::REMOTE_OOB_DATA_REQUEST:
      parse_remote_oob_data_request(event);
      break;
    case EventCode::SIMPLE_PAIRING_COMPLETE:
      parse_simple_pairing_complete(event);
      break;
    case EventCode::USER_PASSKEY_NOTIFICATION:
      parse_user_passkey_notification(event);
      break;
    case EventCode::KEYPRESS_NOTIFICATION:
      parse_keypress_notification(event);
      break;
    case EventCode::USER_CONFIRMATION_REQUEST:
      parse_user_confirmation_request(event);
      break;
    case EventCode::USER_PASSKEY_REQUEST:
      parse_user_passkey_request(event);
      break;
    case EventCode::ENCRYPTION_CHANGE_V2:
      parse_encryption_change_v2(event);
      break;
    default:
      log::error("Unhandled event {}", EventCodeText(event.GetEventCode()));
  }
}
}  // namespace bluetooth::stack::btm
