# SPDX-FileCopyrightText: 2026-present U.N. Owen <void@some.where>
#
# SPDX-License-Identifier: MIT

import json
from pathlib import Path
from typing import Union

from solana.rpc.api import Client
from solana.rpc.commitment import Confirmed
from solana.rpc.types import TxOpts
from solders.instruction import Instruction
from solders.keypair import Keypair
from solders.message import MessageV0
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.transaction import VersionedTransaction

DEFAULT_RPC = "https://api.mainnet-beta.solana.com"


def send_sol(
    wallet_json_path: Union[str, Path],
    to_address: Union[str, Pubkey],
    amount_sol: float,
    rpc_url: str = DEFAULT_RPC,
    commitment=Confirmed,
    auto_adjust_for_rent: bool = True,
) -> str:
    """
    Sends SOL from a JSON-formatted wallet file to a specified address (using VersionedTransaction).

    Args:

        wallet_json_path: Path to the JSON-formatted wallet file (an array of [uint8] exported from Phantom/Solflare, etc.)

        to_address: Recipient address (string or Pubkey)

        amount_sol: Amount of SOL sent (floating-point, e.g., 0.42)

        rpc_url: Solana RPC node, default is the mainnet

        commitment: Confirmation level, default is Confirmed

        auto_adjust_for_rent: Whether to automatically top up the rental fee creation fee

    Returns:

        Transaction signature string (tx signature)

    Raises:

        FileNotFoundError, ValueError, etc.
    """

    path = Path(wallet_json_path)
    if not path.is_file():
        raise FileNotFoundError(f"Wallet file does not exist: {path}")

    with path.open("r") as f:
        secret = json.load(f)

    if not isinstance(secret, list) or len(secret) != 64:
        raise ValueError(
            "Invalid JSON wallet format; should be a list of 64 uint8 elements."
        )

    keypair = Keypair.from_bytes(bytes(secret))

    client = Client(rpc_url)

    balance_lamports = client.get_balance(keypair.pubkey()).value
    lamports_to_send = int(amount_sol * 1_000_000_000)
    if balance_lamports < lamports_to_send + 10_000:
        raise ValueError(
            f"Insufficient balance:{balance_lamports / 1e9:.6f}SOL is available, but requires approximately {amount_sol:.6f} SOL + fees."
        )

    if isinstance(to_address, str):
        to_pubkey = Pubkey.from_string(to_address)
    else:
        to_pubkey = to_address

    to_balance_resp = client.get_balance(to_pubkey)
    to_balance_lamports = to_balance_resp.value

    rent_exempt_lamports = 0
    if to_balance_lamports == 0:
        rent_resp = client.get_minimum_balance_for_rent_exemption(0)
        rent_exempt_lamports = rent_resp.value

        print(
            f"New account creation detected: Requires at least {rent_exempt_lamports / 1e9:.8f} SOL"
        )

        if auto_adjust_for_rent:
            if lamports_to_send < rent_exempt_lamports:
                lamports_to_send = rent_exempt_lamports
                print(
                    f"Automatically adjust the amount sent to the minimum value of rent-exempt: {lamports_to_send / 1e9:.8f} SOL"
                )
        else:
            if lamports_to_send < rent_exempt_lamports:
                raise ValueError(
                    f"Insufficient funds to create a new account: Requires at least {rent_exempt_lamports / 1e9:.8f} SOL，"
                    f"You enter {amount_sol:.8f} SOL. Enabling auto_adjust_for_rent=True will auto-complete the data."
                )

    instruction = transfer(
        TransferParams(
            from_pubkey=keypair.pubkey(),
            to_pubkey=to_pubkey,
            lamports=lamports_to_send,
        )
    )

    recent_blockhash = client.get_latest_blockhash(commitment).value.blockhash

    message = MessageV0.try_compile(
        payer=keypair.pubkey(),
        instructions=[instruction],
        address_lookup_table_accounts=[],
        recent_blockhash=recent_blockhash,
    )

    tx = VersionedTransaction(message, [keypair])

    opts = TxOpts(
        skip_preflight=False,
        preflight_commitment=commitment,
        skip_confirmation=False,
    )
    response = client.send_transaction(tx, opts=opts)
    tx_sig = response.value

    print(f"Transaction sent: https://solscan.io/tx/{tx_sig}")

    client.confirm_transaction(tx_sig, commitment=commitment)

    return str(tx_sig)
