/**
 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

#include "acl/acl_platform.h"

#include <cstring>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <securec.h>

#include "platform/platform_info.h"
#include "platform/platform_infos_def.h"
#include "platform_log.h"

namespace {
aclError CopyToBuffer(const std::string &src, char *dst, uint32_t max_len)
{
  if (src.size() + 1U > static_cast<size_t>(max_len)) {
    PF_LOGE("Buffer too small: need %zu bytes, got %u.", src.size() + 1U, max_len);
    return ACL_ERROR_INVALID_PARAM;
  }
  if (memcpy_s(dst, static_cast<size_t>(max_len), src.c_str(), src.size() + 1U) != 0) {
    PF_LOGE("memcpy_s failed.");
    return ACL_ERROR_INVALID_PARAM;
  }
  return ACL_SUCCESS;
}

// Try to obtain a valid PlatFormInfos from the given manager.
// Strategy 1: runtime device binding (device 0).
// Strategy 2: fallback via OptionalInfos soc_version.
// Returns true and fills |platform_infos| on success.
bool TryGetPlatFormInfos(fe::PlatformInfoManager &mgr, fe::PlatFormInfos &platform_infos, std::string &source)
{
  auto IsValid = [&]() -> bool {
    std::string probe;
    return platform_infos.GetPlatformResWithLock("SoCInfo", "ai_core_cnt", probe) && !probe.empty();
  };

  fe::OptionalInfos optional_infos;
  if (mgr.GetPlatformInfoWithOutSocVersion(platform_infos, optional_infos) == 0U && IsValid()) {
    source = "optional_infos";
    return true;
  }

  if (mgr.GetRuntimePlatformInfosByDevice(0U, platform_infos) == 0U && IsValid()) {
    source = "device_binding";
    return true;
  }

  return false;
}

// Query order:
//   1. Instance  – GetPlatformInfoWithOutSocVersion
//   2. Instance  – GetRuntimePlatformInfosByDevice
//   3. GeInstance – GetPlatformInfoWithOutSocVersion
//   4. GeInstance – GetRuntimePlatformInfosByDevice
bool GetPlatFormInfos(fe::PlatFormInfos &platform_infos)
{
  std::string source;
  const char *instance_name = nullptr;

  if (TryGetPlatFormInfos(fe::PlatformInfoManager::Instance(), platform_infos, source)) {
    instance_name = "Instance";
  } else if (TryGetPlatFormInfos(fe::PlatformInfoManager::GeInstance(), platform_infos, source)) {
    instance_name = "GeInstance";
  }

  if (instance_name != nullptr) {
    PF_LOGD("GetPlatFormInfos succeeded: source=%s, path=%s.", instance_name, source.c_str());
    return true;
  }

  PF_LOGE("Failed to obtain PlatFormInfos via Instance/GeInstance device[0] or OptionalInfos.");
  return false;
}

const std::vector<std::pair<std::string, std::string>> kDevInfoTable = {
  /* 0: ACL_PLATFORM_AICORE_CNT        */ {"SoCInfo",        "ai_core_cnt"},
  /* 1: ACL_PLATFORM_AICORE_UB_SIZE    */ {"AICoreSpec",     "ub_size"},
  /* 2: ACL_PLATFORM_CUBE_CORE_CNT     */ {"SoCInfo",        "cube_core_cnt"},
  /* 3: ACL_PLATFORM_VECTOR_CORE_CNT   */ {"SoCInfo",        "vector_core_cnt"},
  /* 4: ACL_PLATFORM_L2_SIZE           */ {"SoCInfo",        "l2_size"},
  /* 5: ACL_PLATFORM_MEMORY_SIZE       */ {"SoCInfo",        "memory_size"},
  /* 6: ACL_PLATFORM_CUBE_FREQ         */ {"AICoreSpec",     "cube_freq"},
  /* 7: ACL_PLATFORM_VEC_FREQ          */ {"VectorCoreSpec", "vec_freq"},
  /* 8: ACL_PLATFORM_BT_SIZE           */ {"AICoreSpec",     "bt_size"},
  /* 9: ACL_PLATFORM_L0_A_SIZE         */ {"AICoreSpec",     "l0_a_size"},
  /* 10: ACL_PLATFORM_L0_B_SIZE        */ {"AICoreSpec",     "l0_b_size"},
  /* 11: ACL_PLATFORM_L0_C_SIZE        */ {"AICoreSpec",     "l0_c_size"},
  /* 12: ACL_PLATFORM_L1_SIZE          */ {"AICoreSpec",     "l1_size"},
  /* 13: ACL_PLATFORM_SOC_VERSION      */ {"version",       "SoC_version"},
  /* 14: ACL_PLATFORM_AIC_VERSION      */ {"version",       "AIC_version"},
  /* 15: ACL_PLATFORM_NPU_ARCH         */ {"version",        "NpuArch"},
  /* 16: ACL_PLATFORM_MEMORY_TYPE      */ {"SoCInfo",        "memory_type"},
};
}  // namespace

aclError aclplatformGetDeviceInfo(aclplatformDevInfo infoType, char *value, uint32_t maxLen)
{
  // --- parameter validation ---
  const uint32_t info_idx = static_cast<uint32_t>(infoType);
  if (info_idx >= static_cast<uint32_t>(kDevInfoTable.size())) {
    PF_LOGE("Invalid info type %u.", info_idx);
    return ACL_ERROR_INVALID_PARAM;
  }
  if (value == nullptr) {
    PF_LOGE("Device info: output buffer pointer is NULL.");
    return ACL_ERROR_INVALID_PARAM;
  }
  if (maxLen == 0U) {
    PF_LOGE("Device info: output buffer length is 0.");
    return ACL_ERROR_INVALID_PARAM;
  }

  // --- obtain platform info ---
  fe::PlatFormInfos platform_infos;
  if (!GetPlatFormInfos(platform_infos)) {
    return ACL_ERROR_INTERNAL_ERROR;
  }

  // --- query the value ---
  const std::string &section = kDevInfoTable[info_idx].first;
  const std::string &key     = kDevInfoTable[info_idx].second;
  std::string result;

  if (!platform_infos.GetPlatformResWithLock(section, key, result)) {
    PF_LOGE("Key [%s/%s] not found in platform info.", section.c_str(), key.c_str());
    return ACL_ERROR_INVALID_PARAM;
  }
  if (result.empty()) {
    PF_LOGE("Empty result for key [%s/%s].", section.c_str(), key.c_str());
    return ACL_ERROR_INVALID_PARAM;
  }

  PF_LOGI("Device info queried: [%s, %s] = %s.", section.c_str(), key.c_str(), result.c_str());
  return CopyToBuffer(result, value, maxLen);
}

aclError aclplatformGetInstructionInfo(aclplatformCoreType type,
                                       const char *instruction,
                                       char *value,
                                       uint32_t maxLen)
{
  // --- parameter validation ---
  if (type != ACL_PLATFORM_CORE_TYPE_AI_CORE && type != ACL_PLATFORM_CORE_TYPE_VECTOR_CORE) {
    PF_LOGE("Invalid core type %d.", static_cast<int>(type));
    return ACL_ERROR_INVALID_PARAM;
  }
  if (instruction == nullptr) {
    PF_LOGE("Instruction info: instruction pointer is NULL.");
    return ACL_ERROR_INVALID_PARAM;
  }
  if (value == nullptr) {
    PF_LOGE("Instruction info: output buffer pointer is NULL.");
    return ACL_ERROR_INVALID_PARAM;
  }
  if (maxLen == 0U) {
    PF_LOGE("Instruction info: output buffer length is 0.");
    return ACL_ERROR_INVALID_PARAM;
  }

  // --- obtain platform info ---
  fe::PlatFormInfos platform_infos;
  if (!GetPlatFormInfos(platform_infos)) {
    return ACL_ERROR_INTERNAL_ERROR;
  }

  // --- query intrinsic dtype map ---
  const std::string instr_name(instruction);
  std::map<std::string, std::vector<std::string>> intrinsic_map;

  if (type == ACL_PLATFORM_CORE_TYPE_AI_CORE) {
    intrinsic_map = platform_infos.GetAICoreIntrinsicDtype();
  } else {
    intrinsic_map = platform_infos.GetVectorCoreIntrinsicDtype();
  }

  const auto it = intrinsic_map.find(instr_name);
  if (it == intrinsic_map.end() || it->second.empty()) {
    PF_LOGE("Instruction [%s] not found for core type %d.",
            instruction, static_cast<int>(type));
    return ACL_ERROR_INVALID_PARAM;
  }

  // --- join dtypes with "," ---
  std::string result;
  const std::vector<std::string> &dtypes = it->second;
  for (size_t i = 0U; i < dtypes.size(); ++i) {
    if (i > 0U) {
      result += ',';
    }
    result += dtypes[i];
  }

  PF_LOGI("Instruction info queried: [%s], core[%d], result = %s.",
         instruction, static_cast<int>(type), result.c_str());
  return CopyToBuffer(result, value, maxLen);
}