#!/usr/bin/env python3
#
# Sends a specified amount of TR1T
# from a player to any Rebased L1 address
#

import os
import sys
import datetime
import json
import subprocess
from decimal import Decimal, ROUND_DOWN

IOTA = os.environ.get("IOTA_CLI", os.path.join(os.path.expanduser("~"), ".cargo", "bin", "iota"))

# ---- Config (minimal, inline) ----
DECIMALS = 9
GAS_BUDGET = "5000000"
TR1T_TYPE = "0xf4d403ce2cb2d0a0b05af6945167acd933644c6c835dff577ae14e52edbf5bfd::tungsten::TUNGSTEN"

def eprint(msg: str):
    print(msg, file=sys.stderr)

def run(cmd: list[str]) -> subprocess.CompletedProcess:
    return subprocess.run(cmd, capture_output=True, text=True)

def ensure_active_sender(sender_arg: str) -> str:
    # Accept "Master" as-is; otherwise use alias "u<user_id>"
    alias = sender_arg if sender_arg == "Master" or sender_arg.startswith("u") else f"u{sender_arg}"
    r = run([IOTA, "client", "switch", "--address", alias])
    if r.returncode != 0:
        raise RuntimeError(f"Failed to switch to sender alias '{alias}': {r.stderr.strip()}")
    return alias

def list_tr1t_coins() -> list[dict]:
    r = run([IOTA, "client", "objects", "--json"])
    if r.returncode != 0:
        raise RuntimeError(f"Failed to list objects: {r.stderr.strip()}")
    try:
        data = json.loads(r.stdout)
    except json.JSONDecodeError:
        # Older CLI can return a JSON array of {data:{...}}; try to coerce
        data = json.loads(r.stdout.strip())
    coins = []
    for entry in data:
        obj = entry.get("data", {})
        if obj.get("type") == f"0x2::coin::Coin<{TR1T_TYPE}>":
            fields = obj.get("content", {}).get("fields", {})
            bal = int(fields.get("balance", "0"))
            coins.append({"objectId": obj.get("objectId"), "balance": bal})
    return coins

def to_nanos(amount_tr1t: str) -> int:
    amt = Decimal(amount_tr1t)
    if amt <= 0:
        raise ValueError("Amount must be > 0")
    return int((amt * (Decimal(10) ** DECIMALS)).quantize(Decimal(1), rounding=ROUND_DOWN))

def split_coin(coin_id: str, nanos: int) -> str:
    r = run([
        IOTA, "client", "split-coin",
        "--coin-id", coin_id,
        "--amounts", str(nanos),
        "--gas-budget", GAS_BUDGET,
        "--json"
    ])
    if r.returncode != 0:
        raise RuntimeError(f"split-coin failed: {r.stderr.strip()}")
    j = json.loads(r.stdout)
    # Prefer objectChanges for created TR1T coin id
    for ch in j.get("objectChanges", []):
        if ch.get("type") == "created" and ch.get("objectType") == f"0x2::coin::Coin<{TR1T_TYPE}>":
            return ch["objectId"]
    # Fallback: effects.created[].reference.objectId (older schema)
    for created in j.get("effects", {}).get("created", []):
        if created.get("owner") and created.get("reference", {}).get("objectId"):
            return created["reference"]["objectId"]
    raise RuntimeError("Could not find created TR1T coin id in split-coin output.")

def transfer_object(to_addr: str, object_id: str) -> str:
    r = run([
        IOTA, "client", "transfer",
        "--to", to_addr,
        "--object-id", object_id,
        "--gas-budget", GAS_BUDGET,
        "--json"
    ])
    if r.returncode != 0:
        raise RuntimeError(f"transfer failed: {r.stderr.strip()}")
    return r.stdout.strip()

def main():
#    if len(sys.argv) < 4:
#        print("Usage: send_tungsten.py <sender's user ID> <recipient's iota address> <amount>")
#        sys.exit(1)

    sender = sys.argv[1]
    recipient_address = sys.argv[2]
    amount_str = sys.argv[3]

#    sender = "Master"
#    recipient_address = "0x5f0ea3aaa37a57727b6fb8ad92b8f7ebc0134c671b804787d2710a9fef80d084"
#    amount_str = "100"

    # Convert to nanos (TR1T has 9 decimals)
    try:
        amount_nanos = to_nanos(amount_str)
    except Exception as e:
        eprint(f"Invalid amount '{amount_str}': {e}")
        sys.exit(1)

    eprint(f"{datetime.datetime.now()}: Preparing to send {amount_nanos} nano-TR1T from {sender} to {recipient_address}...")

    try:
        alias = ensure_active_sender(sender)
        eprint(f"Active sender alias: {alias}")

        coins = list_tr1t_coins()
        if not coins:
            raise RuntimeError("Sender has no TR1T coins.")

        # Pick the first coin with sufficient balance; else we’ll try to merge or fail (keep minimal)
        chosen = next((c for c in coins if c["balance"] >= amount_nanos), None)
        if not chosen:
            raise RuntimeError("Insufficient TR1T balance in any single coin (merge not implemented in this minimal version).")

        # Exact match: transfer the whole coin object
        if chosen["balance"] == amount_nanos:
            eprint(f"Transferring whole coin {chosen['objectId']} ...")
            out = transfer_object(recipient_address, chosen["objectId"])
            print(out, flush=True)
            return

        # Otherwise split off the desired amount and transfer the split
        eprint(f"Splitting {amount_nanos} from coin {chosen['objectId']} ...")
        new_id = split_coin(chosen["objectId"], amount_nanos)
        eprint(f"Split created coin: {new_id}. Transferring ...")
        out = transfer_object(recipient_address, new_id)
        print(out, flush=True)

    except Exception as e:
        eprint(f"Transaction failed: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()
