package main

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// BridgeChaincode provides functions for cross-chain asset bridging
type BridgeChaincode struct {
	contractapi.Contract
}

// Asset represents a bridged asset
type Asset struct {
	ID          string `json:"id"`
	Owner       string `json:"owner"`
	Amount      uint64 `json:"amount"`
	TokenType   string `json:"tokenType"`
	Locked      bool   `json:"locked"`
	EvmTxHash   string `json:"evmTxHash,omitempty"`
	RequestID   string `json:"requestId,omitempty"`
}

// InitLedger initializes the ledger with sample assets
func (s *BridgeChaincode) InitLedger(ctx contractapi.TransactionContextInterface) error {
	assets := []Asset{
		{ID: "asset1", Owner: "Org1MSP", Amount: 1000, TokenType: "ERC20", Locked: false},
		{ID: "asset2", Owner: "Org2MSP", Amount: 2000, TokenType: "ERC20", Locked: false},
	}

	for _, asset := range assets {
		assetJSON, err := json.Marshal(asset)
		if err != nil {
			return err
		}

		err = ctx.GetStub().PutState(asset.ID, assetJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}

	return nil
}

// ReadAsset returns the asset stored in the world state with given id
func (s *BridgeChaincode) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
	assetJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return nil, fmt.Errorf("failed to read from world state: %v", err)
	}
	if assetJSON == nil {
		return nil, fmt.Errorf("the asset %s does not exist", id)
	}

	var asset Asset
	err = json.Unmarshal(assetJSON, &asset)
	if err != nil {
		return nil, err
	}

	return &asset, nil
}

// LockAsset locks an asset for bridge transfer to EVM
func (s *BridgeChaincode) LockAsset(
	ctx contractapi.TransactionContextInterface,
	assetId string,
	evmDestination string,
	requestId string,
) error {
	asset, err := s.ReadAsset(ctx, assetId)
	if err != nil {
		return err
	}

	if asset.Locked {
		return fmt.Errorf("asset %s is already locked", assetId)
	}

	asset.Locked = true
	asset.RequestID = requestId
	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}

	// Emit event for EVM bridge to pick up
	err = ctx.GetStub().SetEvent("BridgeToEVM", []byte(fmt.Sprintf(`{"assetId":"%s","amount":%d,"evmRecipient":"%s","requestId":"%s"}`, assetId, asset.Amount, evmDestination, requestId)))
	if err != nil {
		return fmt.Errorf("failed to set event: %v", err)
	}

	return ctx.GetStub().PutState(assetId, assetJSON)
}

// UnlockAsset unlocks an asset after bridge completion
func (s *BridgeChaincode) UnlockAsset(
	ctx contractapi.TransactionContextInterface,
	assetId string,
	evmTxHash string,
) error {
	asset, err := s.ReadAsset(ctx, assetId)
	if err != nil {
		return err
	}

	asset.Locked = false
	asset.EvmTxHash = evmTxHash
	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}

	return ctx.GetStub().PutState(assetId, assetJSON)
}

// BurnAsset burns an asset on Fabric side (mint on EVM side)
func (s *BridgeChaincode) BurnAsset(
	ctx contractapi.TransactionContextInterface,
	assetId string,
	evmRecipient string,
	requestId string,
) error {
	asset, err := s.ReadAsset(ctx, assetId)
	if err != nil {
		return err
	}

	// Emit event for EVM minting
	err = ctx.GetStub().SetEvent("BridgeToEVM", []byte(fmt.Sprintf(`{"assetId":"%s","amount":%d,"evmRecipient":"%s","requestId":"%s","action":"mint"}`, assetId, asset.Amount, evmRecipient, requestId)))
	if err != nil {
		return fmt.Errorf("failed to set event: %v", err)
	}

	// Delete asset (burn)
	return ctx.GetStub().DelState(assetId)
}

// CreateAsset creates a new asset
func (s *BridgeChaincode) CreateAsset(
	ctx contractapi.TransactionContextInterface,
	id string,
	amount uint64,
	tokenType string,
) error {
	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		return fmt.Errorf("failed to get client MSP ID: %v", err)
	}

	asset := Asset{
		ID:        id,
		Owner:     clientMSPID,
		Amount:    amount,
		TokenType: tokenType,
		Locked:    false,
	}

	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}

	return ctx.GetStub().PutState(id, assetJSON)
}

// GetAllAssets returns all assets found in world state
func (s *BridgeChaincode) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
	resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()

	var assets []*Asset
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}

		var asset Asset
		err = json.Unmarshal(queryResponse.Value, &asset)
		if err != nil {
			return nil, err
		}
		assets = append(assets, &asset)
	}

	return assets, nil
}

func main() {
	chaincode, err := contractapi.NewChaincode(&BridgeChaincode{})
	if err != nil {
		fmt.Printf("Error creating bridge chaincode: %v", err)
		return
	}

	if err := chaincode.Start(); err != nil {
		fmt.Printf("Error starting bridge chaincode: %v", err)
	}
}
