#include <bitcoin/feerate.h>
#include <bitcoin/script.h>
#include <closingd/gen_closing_wire.h>
#include <common/close_tx.h>
#include <common/initial_commit_tx.h>
#include <common/utils.h>
#include <errno.h>
#include <gossipd/gen_gossip_wire.h>
#include <inttypes.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/closing_control.h>
#include <lightningd/hsm_control.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/options.h>
#include <lightningd/peer_control.h>
#include <lightningd/subd.h>

static struct amount_sat calc_tx_fee(struct amount_sat sat_in,
				     const struct bitcoin_tx *tx)
{
	struct amount_sat amt, fee = sat_in;
	for (size_t i = 0; i < tx->wtx->num_outputs; i++) {
		amt = bitcoin_tx_output_get_amount(tx, i);
		if (!amount_sat_sub(&fee, fee, amt))
			fatal("Tx spends more than input %s? %s",
			      type_to_string(tmpctx, struct amount_sat, &sat_in),
			      type_to_string(tmpctx, struct bitcoin_tx, tx));
	}
	return fee;
}

/* Is this better than the last tx we were holding?  This can happen
 * even without closingd misbehaving, if we have multiple,
 * interrupted, rounds of negotiation. */
static bool better_closing_fee(struct lightningd *ld,
			       struct channel *channel,
			       const struct bitcoin_tx *tx)
{
	struct amount_sat fee, last_fee, min_fee;
	u64 weight;
	u32 min_feerate;
	bool feerate_unknown;

	/* Calculate actual fee (adds in eliminated outputs) */
	fee = calc_tx_fee(channel->funding, tx);
	last_fee = calc_tx_fee(channel->funding, channel->last_tx);

	log_debug(channel->log, "Their actual closing tx fee is %s"
		 " vs previous %s",
		  type_to_string(tmpctx, struct amount_sat, &fee),
		  type_to_string(tmpctx, struct amount_sat, &last_fee));

	/* Weight once we add in sigs. */
	weight = measure_tx_weight(tx) + 74 * 2;

	/* If we don't have a feerate estimate, this gives feerate_floor */
	min_feerate = feerate_min(ld, &feerate_unknown);

	min_fee = amount_tx_fee(min_feerate, weight);
	if (amount_sat_less(fee, min_fee)) {
		log_debug(channel->log, "... That's below our min %s"
			  " for weight %"PRIu64" at feerate %u",
			  type_to_string(tmpctx, struct amount_sat, &fee),
			  weight, min_feerate);
		return false;
	}

	/* In case of a tie, prefer new over old: this covers the preference
	 * for a mutual close over a unilateral one. */

	/* If we don't know the feerate, prefer higher fee. */
	if (feerate_unknown)
		return amount_sat_greater_eq(fee, last_fee);

	/* Otherwise prefer lower fee. */
	return amount_sat_less_eq(fee, last_fee);
}

static void peer_received_closing_signature(struct channel *channel,
					    const u8 *msg)
{
	struct bitcoin_signature sig;
	struct bitcoin_tx *tx;
	struct lightningd *ld = channel->peer->ld;

	if (!fromwire_closing_received_signature(msg, msg, &sig, &tx)) {
		channel_internal_error(channel, "Bad closing_received_signature %s",
				       tal_hex(msg, msg));
		return;
	}

	/* FIXME: Make sure signature is correct! */
	if (better_closing_fee(ld, channel, tx)) {
		channel_set_last_tx(channel, tx, &sig);
		/* TODO(cdecker) Selectively save updated fields to DB */
		wallet_channel_save(ld->wallet, channel);
	}

	/* OK, you can continue now. */
	subd_send_msg(channel->owner,
		      take(towire_closing_received_signature_reply(channel)));
}

static void peer_closing_complete(struct channel *channel, const u8 *msg)
{
	if (!fromwire_closing_complete(msg)) {
		channel_internal_error(channel, "Bad closing_complete %s",
				       tal_hex(msg, msg));
		return;
	}

	/* Don't report spurious failure when closingd exits. */
	channel_set_owner(channel, NULL, false);
	/* Clear any transient negotiation messages */
	channel_set_billboard(channel, false, NULL);

	/* Retransmission only, ignore closing. */
	if (channel->state == CLOSINGD_COMPLETE)
		return;

	/* Channel gets dropped to chain cooperatively. */
	drop_to_chain(channel->peer->ld, channel, true);
	channel_set_state(channel, CLOSINGD_SIGEXCHANGE, CLOSINGD_COMPLETE);
}

static unsigned closing_msg(struct subd *sd, const u8 *msg, const int *fds UNUSED)
{
	enum closing_wire_type t = fromwire_peektype(msg);

	switch (t) {
	case WIRE_CLOSING_RECEIVED_SIGNATURE:
		peer_received_closing_signature(sd->channel, msg);
		break;

	case WIRE_CLOSING_COMPLETE:
		peer_closing_complete(sd->channel, msg);
		break;

	/* We send these, not receive them */
	case WIRE_CLOSING_INIT:
	case WIRE_CLOSING_RECEIVED_SIGNATURE_REPLY:
		break;
	}

	return 0;
}

void peer_start_closingd(struct channel *channel,
			 const struct crypto_state *cs,
			 int peer_fd, int gossip_fd,
			 bool reconnected,
			 const u8 *channel_reestablish)
{
	u8 *initmsg;
	u32 feerate;
	struct amount_sat minfee, startfee, feelimit;
	u64 num_revocations;
	struct amount_msat their_msat;
	int hsmfd;
	struct lightningd *ld = channel->peer->ld;

	if (!channel->remote_shutdown_scriptpubkey) {
		channel_internal_error(channel,
				       "Can't start closing: no remote info");
		return;
	}

	hsmfd = hsm_get_client_fd(ld, &channel->peer->id, channel->dbid,
				  HSM_CAP_SIGN_CLOSING_TX);

	channel_set_owner(channel,
			  new_channel_subd(ld,
					   "lightning_closingd",
					   channel, channel->log, true,
					   closing_wire_type_name, closing_msg,
					   channel_errmsg,
					   channel_set_billboard,
					   take(&peer_fd), take(&gossip_fd),
					   take(&hsmfd),
					   NULL),
			  false);

	if (!channel->owner) {
		log_unusual(channel->log, "Could not subdaemon closing: %s",
			    strerror(errno));
		channel_fail_transient(channel, "Failed to subdaemon closing");
		return;
	}

	/* BOLT #2:
	 *
	 * The sending node:
	 *  - MUST set `fee_satoshis` less than or equal to the base
	 *    fee of the final commitment transaction, as calculated in
	 *    [BOLT #3](03-transactions.md#fee-calculation).
	 */
	feelimit = commit_tx_base_fee(channel->channel_info.feerate_per_kw[LOCAL],
				      0);

	/* Pick some value above slow feerate (or min possible if unknown) */
	minfee = commit_tx_base_fee(feerate_min(ld, NULL), 0);

	/* If we can't determine feerate, start at half unilateral feerate. */
	feerate = mutual_close_feerate(ld->topology);
	if (!feerate) {
		feerate = channel->channel_info.feerate_per_kw[LOCAL] / 2;
		if (feerate < feerate_floor())
			feerate = feerate_floor();
	}
	startfee = commit_tx_base_fee(feerate, 0);

	if (amount_sat_greater(startfee, feelimit))
		startfee = feelimit;
	if (amount_sat_greater(minfee, feelimit))
		minfee = feelimit;

	num_revocations
		= revocations_received(&channel->their_shachain.chain);

	/* BOLT #3:
	 *
	 * Each node offering a signature:
	 *  - MUST round each output down to whole satoshis.
	 */
	/* What is not ours is theirs */
	if (!amount_sat_sub_msat(&their_msat,
				 channel->funding, channel->our_msat)) {
		log_broken(channel->log, "our_msat overflow funding %s minus %s",
			  type_to_string(tmpctx, struct amount_sat,
					 &channel->funding),
			  type_to_string(tmpctx, struct amount_msat,
					 &channel->our_msat));
		channel_fail_permanent(channel, "our_msat overflow on closing");
		return;
	}
	initmsg = towire_closing_init(tmpctx,
				      cs,
				      &channel->funding_txid,
				      channel->funding_outnum,
				      channel->funding,
				      &channel->local_funding_pubkey,
				      &channel->channel_info.remote_fundingkey,
				      channel->funder,
				      amount_msat_to_sat_round_down(channel->our_msat),
				      amount_msat_to_sat_round_down(their_msat),
				      channel->our_config.dust_limit,
				      minfee, feelimit, startfee,
				      p2wpkh_for_keyidx(tmpctx, ld,
							channel->final_key_idx),
				      channel->remote_shutdown_scriptpubkey,
				      reconnected,
				      channel->next_index[LOCAL],
				      channel->next_index[REMOTE],
				      num_revocations,
				      channel_reestablish,
				      p2wpkh_for_keyidx(tmpctx, ld,
							channel->final_key_idx));

	/* We don't expect a response: it will give us feedback on
	 * signatures sent and received, then closing_complete. */
	subd_send_msg(channel->owner, take(initmsg));

	/* Now tell gossipd that we're closing and that neither direction should
	 * be used. */
	if (channel->scid)
		subd_send_msg(channel->peer->ld->gossip,
			      take(towire_gossip_local_channel_close(
				  tmpctx, channel->scid)));
}