defmodule BlockScoutWeb.CouponView do
  @moduledoc """
  Blockscout 定制模块：券NFT详情页扩展
  解析 CouponFactory / Coupon 合约事件，展示券业务字段

  功能清单 (P0):
  - 面值 (face_value)
  - 券类型 (Utility / Security)
  - 到期日 (expiry_date)
  - 转售计数 (resale_count / max_resale)
  - 发行方 (issuer)
  - 可转让性 (transferable)
  """

  alias Explorer.Chain.{Token, TokenTransfer}
  alias Explorer.SmartContract.Reader

  # CouponBatchMinted 事件签名
  @coupon_batch_minted_topic "0x" <>
    Base.encode16(:crypto.hash(:keccak256, "CouponBatchMinted(uint256,address,uint8,uint256,uint256,uint256)"), case: :lower)

  # Coupon 合约 ABI 片段（只读函数）
  @coupon_abi [
    %{
      "name" => "getConfig",
      "type" => "function",
      "stateMutability" => "view",
      "inputs" => [%{"name" => "tokenId", "type" => "uint256"}],
      "outputs" => [
        %{"name" => "issuer", "type" => "address"},
        %{"name" => "faceValue", "type" => "uint256"},
        %{"name" => "couponType", "type" => "uint8"},
        %{"name" => "expiryDate", "type" => "uint256"},
        %{"name" => "maxResaleCount", "type" => "uint256"},
        %{"name" => "transferable", "type" => "bool"}
      ]
    },
    %{
      "name" => "getResaleCount",
      "type" => "function",
      "stateMutability" => "view",
      "inputs" => [%{"name" => "tokenId", "type" => "uint256"}],
      "outputs" => [%{"name" => "", "type" => "uint256"}]
    }
  ]

  @doc "渲染券NFT详情页数据"
  def render_coupon_detail(token_id, contract_address) do
    with {:ok, config} <- call_get_config(contract_address, token_id),
         {:ok, resale_count} <- call_get_resale_count(contract_address, token_id) do
      %{
        token_id: token_id,
        face_value: config.face_value,
        coupon_type: decode_coupon_type(config.coupon_type),
        expiry_date: DateTime.from_unix!(config.expiry_date),
        max_resale_count: config.max_resale_count,
        resale_count: resale_count,
        transferable: config.transferable,
        issuer: config.issuer,
        expired: DateTime.utc_now() > DateTime.from_unix!(config.expiry_date)
      }
    else
      {:error, reason} -> {:error, reason}
    end
  end

  @doc "解析 CouponBatchMinted 事件日志"
  def parse_batch_minted_event(log) do
    case log.first_topic do
      @coupon_batch_minted_topic ->
        %{
          batch_id: decode_uint256(log.second_topic),
          issuer: decode_address(log.third_topic),
          coupon_type: decode_coupon_type(decode_uint8_from_data(log.data, 0)),
          face_value: decode_uint256_from_data(log.data, 1),
          quantity: decode_uint256_from_data(log.data, 2),
          start_token_id: decode_uint256_from_data(log.data, 3)
        }

      _ ->
        nil
    end
  end

  @doc "获取券批次摘要信息"
  def get_batch_summary(batch_id, contract_address) do
    %{
      batch_id: batch_id,
      contract: contract_address,
      minted_event: find_batch_minted_event(batch_id, contract_address),
      holder_count: count_current_holders(batch_id, contract_address)
    }
  end

  # ── 私有函数 ──

  defp call_get_config(contract_address, token_id) do
    case Reader.query_contract(contract_address, @coupon_abi, %{
           "getConfig" => [token_id]
         }) do
      %{"getConfig" => {:ok, [issuer, face_value, coupon_type, expiry_date, max_resale, transferable]}} ->
        {:ok,
         %{
           issuer: issuer,
           face_value: face_value,
           coupon_type: coupon_type,
           expiry_date: expiry_date,
           max_resale_count: max_resale,
           transferable: transferable
         }}

      _ ->
        {:error, :contract_call_failed}
    end
  end

  defp call_get_resale_count(contract_address, token_id) do
    case Reader.query_contract(contract_address, @coupon_abi, %{
           "getResaleCount" => [token_id]
         }) do
      %{"getResaleCount" => {:ok, [count]}} -> {:ok, count}
      _ -> {:error, :contract_call_failed}
    end
  end

  defp decode_coupon_type(0), do: :utility
  defp decode_coupon_type(1), do: :security
  defp decode_coupon_type(_), do: :unknown

  defp decode_uint256(nil), do: 0
  defp decode_uint256("0x" <> hex), do: String.to_integer(hex, 16)

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

  defp decode_uint256_from_data(data, offset) do
    data
    |> String.slice(2 + offset * 64, 64)
    |> String.to_integer(16)
  end

  defp decode_uint8_from_data(data, offset) do
    decode_uint256_from_data(data, offset)
  end

  defp find_batch_minted_event(_batch_id, _contract_address) do
    # 查询链上日志获取铸造事件
    nil
  end

  defp count_current_holders(_batch_id, _contract_address) do
    # 聚合当前持有人数量
    0
  end
end
