defmodule BlockScoutWeb.ComplianceLabels do
  @moduledoc """
  Blockscout 定制模块：合规标签系统
  - 冻结地址标红 (frozen → red)
  - 可疑交易标橙 (suspicious → orange)
  - Travel Rule 交易标记
  - OFAC 命中高亮
  """

  alias Explorer.SmartContract.Reader

  # Compliance 合约 ABI 片段
  @compliance_abi [
    %{
      "name" => "isFrozen",
      "type" => "function",
      "stateMutability" => "view",
      "inputs" => [%{"name" => "account", "type" => "address"}],
      "outputs" => [%{"name" => "", "type" => "bool"}]
    },
    %{
      "name" => "getKYCLevel",
      "type" => "function",
      "stateMutability" => "view",
      "inputs" => [%{"name" => "account", "type" => "address"}],
      "outputs" => [%{"name" => "", "type" => "uint8"}]
    },
    %{
      "name" => "isOFACListed",
      "type" => "function",
      "stateMutability" => "view",
      "inputs" => [%{"name" => "account", "type" => "address"}],
      "outputs" => [%{"name" => "", "type" => "bool"}]
    }
  ]

  # 事件签名
  @address_frozen_topic "0x" <>
    Base.encode16(:crypto.hash(:keccak256, "AddressFrozen(address,string)"), case: :lower)

  @suspicious_activity_topic "0x" <>
    Base.encode16(:crypto.hash(:keccak256, "SuspiciousActivity(address,uint256,string)"), case: :lower)

  @travel_rule_topic "0x" <>
    Base.encode16(:crypto.hash(:keccak256, "TravelRuleRecord(bytes32,address,address,uint256)"), case: :lower)

  @type label :: %{
          type: :frozen | :suspicious | :travel_rule | :ofac_hit,
          severity: :critical | :warning | :info,
          color: String.t(),
          text: String.t(),
          tooltip: String.t()
        }

  @doc "获取地址的所有合规标签"
  @spec get_address_labels(String.t(), String.t()) :: [label()]
  def get_address_labels(address, compliance_contract) do
    labels = []

    labels =
      if is_frozen?(address, compliance_contract) do
        [
          %{
            type: :frozen,
            severity: :critical,
            color: "#DC2626",
            text: "FROZEN",
            tooltip: "此地址已被冻结，所有交易将被拒绝"
          }
          | labels
        ]
      else
        labels
      end

    labels =
      if is_ofac_listed?(address, compliance_contract) do
        [
          %{
            type: :ofac_hit,
            severity: :critical,
            color: "#DC2626",
            text: "OFAC",
            tooltip: "此地址命中 OFAC 制裁名单"
          }
          | labels
        ]
      else
        labels
      end

    labels =
      case get_kyc_level(address, compliance_contract) do
        0 ->
          [
            %{
              type: :suspicious,
              severity: :warning,
              color: "#F97316",
              text: "NO KYC",
              tooltip: "此地址未完成 KYC 验证"
            }
            | labels
          ]

        _ ->
          labels
      end

    labels
  end

  @doc "获取交易的合规标签"
  @spec get_transaction_labels(map()) :: [label()]
  def get_transaction_labels(transaction) do
    labels = []

    labels =
      if has_travel_rule_record?(transaction) do
        [
          %{
            type: :travel_rule,
            severity: :info,
            color: "#3B82F6",
            text: "TRAVEL RULE",
            tooltip: "此交易包含 Travel Rule 合规记录"
          }
          | labels
        ]
      else
        labels
      end

    labels =
      if is_suspicious_transaction?(transaction) do
        [
          %{
            type: :suspicious,
            severity: :warning,
            color: "#F97316",
            text: "SUSPICIOUS",
            tooltip: "此交易被标记为可疑（AI 分析或规则触发）"
          }
          | labels
        ]
      else
        labels
      end

    labels
  end

  @doc "检查日志是否包含合规事件"
  def parse_compliance_event(log) do
    case log.first_topic do
      @address_frozen_topic ->
        {:frozen, %{address: decode_address(log.second_topic)}}

      @suspicious_activity_topic ->
        {:suspicious, %{address: decode_address(log.second_topic)}}

      @travel_rule_topic ->
        {:travel_rule, %{record_hash: log.second_topic}}

      _ ->
        nil
    end
  end

  # ── 私有函数 ──

  defp is_frozen?(address, contract) do
    case Reader.query_contract(contract, @compliance_abi, %{"isFrozen" => [address]}) do
      %{"isFrozen" => {:ok, [true]}} -> true
      _ -> false
    end
  end

  defp is_ofac_listed?(address, contract) do
    case Reader.query_contract(contract, @compliance_abi, %{"isOFACListed" => [address]}) do
      %{"isOFACListed" => {:ok, [true]}} -> true
      _ -> false
    end
  end

  defp get_kyc_level(address, contract) do
    case Reader.query_contract(contract, @compliance_abi, %{"getKYCLevel" => [address]}) do
      %{"getKYCLevel" => {:ok, [level]}} -> level
      _ -> 0
    end
  end

  defp has_travel_rule_record?(transaction) do
    Enum.any?(transaction.logs || [], fn log ->
      log.first_topic == @travel_rule_topic
    end)
  end

  defp is_suspicious_transaction?(transaction) do
    Enum.any?(transaction.logs || [], fn log ->
      log.first_topic == @suspicious_activity_topic
    end)
  end

  defp decode_address("0x000000000000000000000000" <> addr), do: "0x" <> addr
  defp decode_address(other), do: other
end
