package chains

import (
	"context"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/ethereum/go-ethereum/rpc"
	"github.com/mr-tron/base58"

	"github.com/wormhole-foundation/wormhole-explorer/txtracker/config"
)

type solanaTransactionSignature struct {
	Signature string `json:"signature"`
}

type solanaGetTransactionResponse struct {
	BlockTime   int64                 `json:"blockTime"`
	Meta        solanaTransactionMeta `json:"meta"`
	Transaction solanaTransaction     `json:"transaction"`
}

type solanaTransactionMeta struct {
	InnerInstructions []solanaInnerInstruction `json:"innerInstructions"`
	Err               []interface{}            `json:"err"`
}

type solanaInnerInstruction struct {
	Instructions []solanaInstruction `json:"instructions"`
}

type solanaInstruction struct {
	ParsedInstruction solanaParsedInstruction `json:"parsed"`
}

type solanaParsedInstruction struct {
	Type_ string                      `json:"type"`
	Info  solanaParsedInstructionInfo `json:"info"`
}

type solanaParsedInstructionInfo struct {
	Account     string `json:"account"`
	Amount      string `json:"amount"`
	Authority   string `json:"authority"`
	Destination string `json:"destination"`
	Source      string `json:"source"`
}

type solanaTransaction struct {
	Message    solanaTransactionMessage `json:"message"`
	Signatures []string                 `json:"signatures"`
}

type solanaTransactionMessage struct {
	AccountKeys []solanaAccountKey `json:"accountKeys"`
}

type solanaAccountKey struct {
	Pubkey string `json:"pubkey"`
	Signer bool   `json:"signer"`
}

func fetchSolanaTx(
	ctx context.Context,
	cfg *config.RpcProviderSettings,
	txHash string,
) (*TxDetail, error) {

	// Initialize RPC client
	client, err := rpc.DialContext(ctx, cfg.SolanaBaseUrl)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize RPC client: %w", err)
	}
	defer client.Close()

	// Decode txHash bytes
	h, err := hex.DecodeString(txHash)
	if err != nil {
		return nil, fmt.Errorf("failed to decode from hex txHash=%s: %w", txHash, err)
	}

	// Get transaction signatures for the given account
	var sigs []solanaTransactionSignature
	err = client.CallContext(ctx, &sigs, "getSignaturesForAddress", base58.Encode(h))
	if err != nil {
		return nil, fmt.Errorf("failed to get signatures for account: %w (%+v)", err, err)
	}
	if len(sigs) == 0 {
		return nil, ErrTransactionNotFound
	}
	if len(sigs) > 1 {
		return nil, fmt.Errorf("expected exactly one signature, but found %d", len(sigs))
	}

	// Fetch the portal token bridge transaction
	var response solanaGetTransactionResponse
	err = client.CallContext(ctx, &response, "getTransaction", sigs[0].Signature, "jsonParsed")
	if err != nil {
		return nil, fmt.Errorf("failed to get tx by signature: %w", err)
	}
	if len(response.Meta.InnerInstructions) == 0 {
		return nil, fmt.Errorf("response.Meta.InnerInstructions is empty")
	}
	if len(response.Meta.InnerInstructions[0].Instructions) == 0 {
		return nil, fmt.Errorf("response.Meta.InnerInstructions[0].Instructions is empty")
	}

	// populate the response object
	txDetail := TxDetail{
		Timestamp:    time.Unix(response.BlockTime, 0).UTC(),
		NativeTxHash: sigs[0].Signature,
	}

	// set sender/receiver
	for i := range response.Transaction.Message.AccountKeys {
		if response.Transaction.Message.AccountKeys[i].Signer {
			txDetail.Signer = response.Transaction.Message.AccountKeys[i].Pubkey
		}
	}
	if txDetail.Signer == "" {
		return nil, fmt.Errorf("failed to find source account")
	}

	return &txDetail, nil
}
