require "rqrcode"
require "lnurl"

class Services::LightningController < ApplicationController
  before_action :authenticate_user!
  before_action :authenticate_with_lndhub
  before_action :set_current_section
  before_action :fetch_balance

  def index
    @wallet_setup_url = "lndhub://#{current_user.ln_account}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
  end

  def transactions
    @transactions = fetch_transactions
  end

  def qr_lnurlp
    lnurlp_url = "https://kosmos.org/.well-known/lnurlp/#{current_user.cn}"
    lnurlp_bech32 = Lnurl.new(lnurlp_url).to_bech32
    qr_code = RQRCode::QRCode.new("lightning:" + lnurlp_bech32)

    respond_to do |format|
      format.svg do
        qr_svg = qr_code.as_svg(
          color: "000",
          shape_rendering: "crispEdges",
          module_size: 6,
          standalone: true,
          use_path: true,
          svg_attributes: {
            class: 'inline-block'
          }
        )
        send_data(
          qr_svg,
          filename: "bitcoin-lightning-#{current_user.address}.svg",
          type: "image/svg+xml"
        )
      end
      format.png do
        qr_png = qr_code.as_png(
          fill: "white",
          color: "black",
          size: 1024,
        )
        send_data(
          qr_png,
          filename: "bitcoin-lightning-#{current_user.address}.png",
          type: "image/png"
        )
      end
    end
  end

  private

  def authenticate_with_lndhub(options={})
    if session[:ln_auth_token].present? && !options[:force_reauth]
      @ln_auth_token = session[:ln_auth_token]
    else
      lndhub = Lndhub.new
      auth_token = lndhub.authenticate(current_user)
      session[:ln_auth_token] = auth_token
      @ln_auth_token = auth_token
    end
  rescue => e
    Sentry.capture_exception(e) if Setting.sentry_enabled?
  end

  def set_current_section
    @current_section = :services
  end

  def fetch_balance
    lndhub = Lndhub.new
    data = lndhub.balance @ln_auth_token
    @balance = data["BTC"]["AvailableBalance"] rescue nil
  rescue AuthError
    authenticate_with_lndhub(force_reauth: true)
    raise if @fetch_balance_retried
    @fetch_balance_retried = true
    fetch_balance
  end

  def fetch_transactions
    lndhub = Lndhub.new
    txs = lndhub.gettxs @ln_auth_token
    invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]}
    process_transactions(txs + invoices)
  rescue AuthError
    authenticate_with_lndhub(force_reauth: true)
    raise if @fetch_transactions_retried
    @fetch_transactions_retried = true
    fetch_transactions
  end

  def process_transactions(txs)
    txs.collect do |tx|
      if tx["type"] == "bitcoind_tx"
        tx["amount_sats"] = (tx["amount"] * 100000000).to_i
        tx["datetime"] = Time.at(tx["time"].to_i)
        tx["title"] = "Received"
        tx["description"] = "On-chain topup"
        tx["received"] = true
      else
        tx["amount_sats"] = tx["value"] || tx["amt"]
        tx["fee"] = tx["type"] == "paid_invoice" ? tx["fee"] : nil
        tx["datetime"] = Time.at(tx["timestamp"].to_i)
        tx["title"] = tx["type"] == "paid_invoice" ? "Sent" : "Received"
        tx["description"] = tx["memo"] || tx["description"]
        tx["received"] = tx["type"] == "user_invoice"
      end
    end

    # Handle an edge case where lndhub.go includes a failed payment in the
    # list, which wasn't actually booked
    txs.reject!{ |tx| tx["type"] == "paid_invoice" && tx["payment_preimage"].blank? }

    txs.sort{ |a,b| b["datetime"] <=> a["datetime"] }
  end
end
