/**
 * Copyright (c) 2025 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.
 */

#ifndef DOMI_ANALYZER_ANANLYZER_H_
#define DOMI_ANALYZER_ANANLYZER_H_

#include "nlohmann/json.hpp"

#include <map>
#include <string>
#include <mutex>
#include <memory>
#include <fstream>
#include <atomic>

#include "ge/ge_api_types.h"
#include "graph/compute_graph.h"
#include "graph/node.h"
#include "mmpa/mmpa_api.h"

namespace ge {
namespace analyzer {
enum AnalyzeType {
  PARSER         = 0,
  INFER_SHAPE    = 1,
  CHECKSUPPORT   = 2,
  GRAPH_OPTIMIZE = 3,
  GRAPH_PARTION  = 4,
  GRAPH_BUILDER  = 5,
};

struct TensorInfo {
  std::vector<int64_t> shape;
  std::string d_type;
  std::string layout;
};

struct OpInfo {
  std::string error_type;
  std::string op_name;
  std::string op_type;
  std::vector<TensorInfo> input_info;
  std::vector<TensorInfo> output_info;
  std::string reason;
};

struct GraphInfo {
  uint64_t session_id = 0;
  uint64_t graph_id = 0;
  std::vector<OpInfo> op_info;
};

struct DataInfo {
  DataInfo() = default;
  ~DataInfo() = default;

  DataInfo(uint64_t sess, uint64_t graph, AnalyzeType type,
           ge::NodePtr node, std::string error_info) {
    session_id = sess;
    graph_id = graph;
    analyze_type = type;
    node_ptr = node;
    reason = error_info;
  }
  uint64_t session_id;
  uint64_t graph_id;
  AnalyzeType analyze_type;
  ge::NodePtr node_ptr{nullptr};
  std::string reason;
};
}

class Analyzer {
public:
  /**
   * @ingroup ge
   * @brief: get analyzer instance.
   * @param [in]: None
   * @return: Analyzer instance ptr
   */
  static Analyzer *GetInstance();

  /**
   * @ingroup ge
   * @brief: check whether env var ENABLE_NETWORK_ANALYSIS_DEBUG is enabled.
   *     When enable env, it will keep adaptor sink geop graph even though fail.
   * @param [in]: None
   * @return: true: enable env   false : disable env
   */
  bool IsEnableNetAnalyzeDebug() {
    const char_t *env = nullptr;
    MM_SYS_GET_ENV(MM_ENV_ENABLE_NETWORK_ANALYSIS_DEBUG, env);
    return env != nullptr;
  }

  /**
   * @ingroup ge
   * @brief: build buff object by sess id and graph id .
   * @param [in]: session id & graph id
   * @return: 0: success other: failed
   */
  ge::Status BuildJsonObject(uint64_t session_id, uint64_t graph_id);

  /**
   * @ingroup ge
   * @brief: get buff object by sess id and graph id .
   * @param [in]: session id & graph id
   * @return: nullptr if failed
   */
  std::shared_ptr<analyzer::GraphInfo> GetJsonObject(uint64_t session_id, uint64_t graph_id);

  /**
   * @ingroup ge
   * @brief: analyzer global init method.
   * @param [in]: None
   * @return: None
   */
  ge::Status Initialize();

  /**
   * @ingroup ge
   * @brief: DeConstruct method. Release all used resource of analyzer.
   * @param [in]: None
   * @return: None
   */
  void Finalize();

  /**
   * @ingroup ge
   * @brief: DeConstruct method. Only release resource about session id.
   * @param [in]: None
   * @return: None
   */
  void DestroySessionJsonObject(uint64_t session_id);

  /**
   * @ingroup ge
   * @brief: DeConstruct method. Only release resource about session id and graph id.
   * @param [in]: None
   * @return: None
   */
  void DestroyGraphJsonObject(uint64_t session_id, uint64_t graph_id);

  /**
   * @ingroup ge
   * @brief: main process method. Buff analyzed data and output to json file
   * @param [in]: DataInfo Object
   * @return: 0: SUCCESS other: FAILED
   */
  ge::Status DoAnalyze(const analyzer::DataInfo &data_info);

  /**
   * @ingroup ge
   * @brief: Buff analyzed data and output to json file
   * @param [in]: session id , graph id
   * @return: 0: SUCCESS other: FAILED
   */
  ge::Status SaveAnalyzerDataToFile(uint64_t session_id, uint64_t graph_id);

  Analyzer(const Analyzer &) = delete;
  Analyzer& operator=(const Analyzer&) = delete;
  Analyzer(Analyzer &&) = delete;
  Analyzer& operator=(Analyzer &&) = delete;
private:
  void TensorInfoToJson(nlohmann::json& j, const analyzer::TensorInfo &tensor_info) const;
  void OpInfoToJson(nlohmann::json& j, const analyzer::OpInfo &op_info) const;
  void GraphInfoToJson(nlohmann::json& j, const analyzer::GraphInfo &graph_info);

  ge::Status SaveOpInfo(ge::OpDescPtr desc, const analyzer::DataInfo &data_info,
                                  std::shared_ptr<analyzer::GraphInfo> graph_info) const;

  void ClearHistoryFile() const;
  ge::Status CreateAnalyzerFile();

  explicit Analyzer() {};
  ~Analyzer() = default;

private:
  std::map<uint64_t, std::map<uint64_t, std::shared_ptr<analyzer::GraphInfo>>> graph_infos_;
  std::recursive_mutex mutex_; // protect graph_infos_
  std::mutex file_mutex_; // protect json_file_
  std::ofstream json_file_;
  std::string json_file_name_;
  std::atomic_bool is_json_file_create_{false};
};
} // namespace ge
#endif // DOMI_ANALYZER_ANANLYZER_H_