// Copyright (C) 2015-2026 The Neo Project.
//
// MainService.Network.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Akka.Actor;
using Neo.ConsoleService;
using Neo.Extensions;
using Neo.IO;
using Neo.Json;
using Neo.Network.P2P;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using System.Net;

namespace Neo.CLI;

partial class MainService
{
    /// <summary>
    /// Process "broadcast addr" command
    /// </summary>
    /// <param name="payload">Payload</param>
    /// <param name="port">Port</param>
    [ConsoleCommand("broadcast addr", Category = "Network Commands")]
    private void OnBroadcastAddressCommand(IPAddress payload, ushort port)
    {
        if (payload == null)
        {
            ConsoleHelper.Warning("You must input the payload to relay.");
            return;
        }

        OnBroadcastCommand(MessageCommand.Addr,
            AddrPayload.Create(
                NetworkAddressWithTime.Create(
                    payload, DateTime.UtcNow.ToTimestamp(),
                    LocalNode.GetNodeCapabilities()
                )));
    }

    /// <summary>
    /// Process "broadcast block" command
    /// </summary>
    /// <param name="hash">Hash</param>
    [ConsoleCommand("broadcast block", Category = "Network Commands")]
    private void OnBroadcastGetBlocksByHashCommand(UInt256 hash)
    {
        Block? block = NativeContract.Ledger.GetBlock(NeoSystem.StoreView, hash);
        if (block is null)
            ConsoleHelper.Error("Block is not found.");
        else
            OnBroadcastCommand(MessageCommand.Block, block);
    }

    /// <summary>
    /// Process "broadcast block" command
    /// </summary>
    /// <param name="height">Block index</param>
    [ConsoleCommand("broadcast block", Category = "Network Commands")]
    private void OnBroadcastGetBlocksByHeightCommand(uint height)
    {
        Block? block = NativeContract.Ledger.GetBlock(NeoSystem.StoreView, height);
        if (block is null)
            ConsoleHelper.Error("Block is not found.");
        else
            OnBroadcastCommand(MessageCommand.Block, block);
    }

    /// <summary>
    /// Process "broadcast getblocks" command
    /// </summary>
    /// <param name="hash">Hash</param>
    [ConsoleCommand("broadcast getblocks", Category = "Network Commands")]
    private void OnBroadcastGetBlocksCommand(UInt256 hash)
    {
        OnBroadcastCommand(MessageCommand.GetBlocks, GetBlocksPayload.Create(hash));
    }

    /// <summary>
    /// Process "broadcast getheaders" command
    /// </summary>
    /// <param name="index">Index</param>
    [ConsoleCommand("broadcast getheaders", Category = "Network Commands")]
    private void OnBroadcastGetHeadersCommand(uint index)
    {
        OnBroadcastCommand(MessageCommand.GetHeaders, GetBlockByIndexPayload.Create(index));
    }

    /// <summary>
    /// Process "broadcast getdata" command
    /// </summary>
    /// <param name="type">Type</param>
    /// <param name="payload">Payload</param>
    [ConsoleCommand("broadcast getdata", Category = "Network Commands")]
    private void OnBroadcastGetDataCommand(InventoryType type, UInt256[] payload)
    {
        OnBroadcastCommand(MessageCommand.GetData, InvPayload.Create(type, payload));
    }

    /// <summary>
    /// Process "broadcast inv" command
    /// </summary>
    /// <param name="type">Type</param>
    /// <param name="payload">Payload</param>
    [ConsoleCommand("broadcast inv", Category = "Network Commands")]
    private void OnBroadcastInvCommand(InventoryType type, UInt256[] payload)
    {
        OnBroadcastCommand(MessageCommand.Inv, InvPayload.Create(type, payload));
    }

    /// <summary>
    /// Process "broadcast transaction" command
    /// </summary>
    /// <param name="hash">Hash</param>
    [ConsoleCommand("broadcast transaction", Category = "Network Commands")]
    private void OnBroadcastTransactionCommand(UInt256 hash)
    {
        if (NeoSystem.MemPool.TryGetValue(hash, out var tx))
            OnBroadcastCommand(MessageCommand.Transaction, tx);
    }

    private void OnBroadcastCommand(MessageCommand command, ISerializable ret)
    {
        NeoSystem.LocalNode.Tell(Message.Create(command, ret));
    }

    /// <summary>
    /// Process "relay" command
    /// </summary>
    /// <param name="jsonObjectToRelay">Json object</param>
    [ConsoleCommand("relay", Category = "Network Commands")]
    private void OnRelayCommand(JObject jsonObjectToRelay)
    {
        if (jsonObjectToRelay == null)
        {
            ConsoleHelper.Warning("You must input JSON object to relay.");
            return;
        }

        try
        {
            ContractParametersContext context = ContractParametersContext.Parse(jsonObjectToRelay.ToString(), NeoSystem.StoreView);
            if (!context.Completed)
            {
                ConsoleHelper.Error("The signature is incomplete.");
                return;
            }
            if (!(context.Verifiable is Transaction tx))
            {
                ConsoleHelper.Warning("Only support to relay transaction.");
                return;
            }
            tx.Witnesses = context.GetWitnesses();
            NeoSystem.Blockchain.Tell(tx);
            Console.WriteLine($"Data relay success, the hash is shown as follows: {Environment.NewLine}{tx.Hash}");
        }
        catch (Exception e)
        {
            ConsoleHelper.Error(GetExceptionMessage(e));
        }
    }
}
