package main

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

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

// ReserveManagerContract provides functions for managing reserves
type ReserveManagerContract struct {
	contractapi.Contract
}

// Reserve represents a reserve backing tokenized assets
type Reserve struct {
	ReserveID      string    `json:"reserveId"`
	AssetType      string    `json:"assetType"`      // EUR, USD, etc.
	TotalAmount    string    `json:"totalAmount"`
	BackedAmount   string    `json:"backedAmount"`   // Amount already backing tokens
	AvailableAmount string   `json:"availableAmount"` // Available for new tokens
	Attestor       string    `json:"attestor"`
	AttestationHash string   `json:"attestationHash"`
	LastVerified   string    `json:"lastVerified"`
	CreatedAt      string    `json:"createdAt"`
	UpdatedAt      string    `json:"updatedAt"`
}

// AttestationRequest represents a request to attest to reserves
type AttestationRequest struct {
	ReserveID      string `json:"reserveId"`
	AssetType      string `json:"assetType"`
	TotalAmount    string `json:"totalAmount"`
	Attestor       string `json:"attestor"`
	AttestationHash string `json:"attestationHash"`
	Proof          string `json:"proof"`
}

// VerifyReserve verifies that reserves are sufficient for tokenization
func (s *ReserveManagerContract) VerifyReserve(ctx contractapi.TransactionContextInterface, reserveID string, amount string) (bool, error) {
	reserveJSON, err := ctx.GetStub().GetState(reserveID)
	if err != nil {
		return false, fmt.Errorf("failed to read reserve from world state: %v", err)
	}
	if reserveJSON == nil {
		return false, fmt.Errorf("reserve %s does not exist", reserveID)
	}

	var reserve Reserve
	err = json.Unmarshal(reserveJSON, &reserve)
	if err != nil {
		return false, err
	}

	// Parse amounts
	requestAmount, err := strconv.ParseFloat(amount, 64)
	if err != nil {
		return false, fmt.Errorf("invalid amount: %v", err)
	}

	availableAmount, err := strconv.ParseFloat(reserve.AvailableAmount, 64)
	if err != nil {
		return false, fmt.Errorf("invalid available amount: %v", err)
	}

	// Check if sufficient reserve available
	if requestAmount > availableAmount {
		return false, fmt.Errorf("insufficient reserve: requested %f, available %f", requestAmount, availableAmount)
	}

	// Verify attestation is recent (within 24 hours)
	lastVerified, err := time.Parse(time.RFC3339, reserve.LastVerified)
	if err != nil {
		return false, fmt.Errorf("invalid last verified timestamp: %v", err)
	}

	if time.Since(lastVerified) > 24*time.Hour {
		return false, fmt.Errorf("reserve attestation is stale (older than 24 hours)")
	}

	return true, nil
}

// CreateReserve creates a new reserve with attestation
func (s *ReserveManagerContract) CreateReserve(ctx contractapi.TransactionContextInterface, requestJSON string) error {
	var request AttestationRequest
	err := json.Unmarshal([]byte(requestJSON), &request)
	if err != nil {
		return fmt.Errorf("failed to unmarshal attestation request: %v", err)
	}

	// Check if reserve already exists
	existing, err := ctx.GetStub().GetState(request.ReserveID)
	if err != nil {
		return fmt.Errorf("failed to read from world state: %v", err)
	}
	if existing != nil {
		return fmt.Errorf("reserve %s already exists", request.ReserveID)
	}

	// Parse total amount
	totalAmount, err := strconv.ParseFloat(request.TotalAmount, 64)
	if err != nil {
		return fmt.Errorf("invalid total amount: %v", err)
	}

	// Create reserve
	reserve := Reserve{
		ReserveID:       request.ReserveID,
		AssetType:       request.AssetType,
		TotalAmount:     request.TotalAmount,
		BackedAmount:    "0.00",
		AvailableAmount: request.TotalAmount,
		Attestor:        request.Attestor,
		AttestationHash: request.AttestationHash,
		LastVerified:    time.Now().Format(time.RFC3339),
		CreatedAt:       time.Now().Format(time.RFC3339),
		UpdatedAt:       time.Now().Format(time.RFC3339),
	}

	reserveJSON, err := json.Marshal(reserve)
	if err != nil {
		return err
	}

	err = ctx.GetStub().PutState(request.ReserveID, reserveJSON)
	if err != nil {
		return fmt.Errorf("failed to put reserve to world state: %v", err)
	}

	// Emit event
	eventPayload := fmt.Sprintf(`{"reserveId":"%s","action":"create","amount":"%s","attestor":"%s"}`,
		request.ReserveID, request.TotalAmount, request.Attestor)
	err = ctx.GetStub().SetEvent("ReserveCreated", []byte(eventPayload))
	if err != nil {
		return fmt.Errorf("failed to emit event: %v", err)
	}

	return nil
}

// AttestReserve updates reserve attestation
func (s *ReserveManagerContract) AttestReserve(ctx contractapi.TransactionContextInterface, requestJSON string) error {
	var request AttestationRequest
	err := json.Unmarshal([]byte(requestJSON), &request)
	if err != nil {
		return fmt.Errorf("failed to unmarshal attestation request: %v", err)
	}

	// Get existing reserve
	reserveJSON, err := ctx.GetStub().GetState(request.ReserveID)
	if err != nil {
		return fmt.Errorf("failed to read reserve from world state: %v", err)
	}
	if reserveJSON == nil {
		return fmt.Errorf("reserve %s does not exist", request.ReserveID)
	}

	var reserve Reserve
	err = json.Unmarshal(reserveJSON, &reserve)
	if err != nil {
		return err
	}

	// Update attestation
	reserve.Attestor = request.Attestor
	reserve.AttestationHash = request.AttestationHash
	reserve.TotalAmount = request.TotalAmount
	
	// Recalculate available amount
	totalAmount, err := strconv.ParseFloat(request.TotalAmount, 64)
	if err != nil {
		return fmt.Errorf("invalid total amount: %v", err)
	}

	backedAmount, err := strconv.ParseFloat(reserve.BackedAmount, 64)
	if err != nil {
		return fmt.Errorf("invalid backed amount: %v", err)
	}

	availableAmount := totalAmount - backedAmount
	reserve.AvailableAmount = fmt.Sprintf("%.2f", availableAmount)
	reserve.LastVerified = time.Now().Format(time.RFC3339)
	reserve.UpdatedAt = time.Now().Format(time.RFC3339)

	updatedJSON, err := json.Marshal(reserve)
	if err != nil {
		return err
	}

	err = ctx.GetStub().PutState(request.ReserveID, updatedJSON)
	if err != nil {
		return fmt.Errorf("failed to update reserve in world state: %v", err)
	}

	// Emit event
	eventPayload := fmt.Sprintf(`{"reserveId":"%s","action":"attest","amount":"%s","attestor":"%s"}`,
		request.ReserveID, request.TotalAmount, request.Attestor)
	err = ctx.GetStub().SetEvent("ReserveAttested", []byte(eventPayload))
	if err != nil {
		return fmt.Errorf("failed to emit event: %v", err)
	}

	return nil
}

// AllocateReserve allocates reserve amount for token backing
func (s *ReserveManagerContract) AllocateReserve(ctx contractapi.TransactionContextInterface, reserveID string, amount string) error {
	reserveJSON, err := ctx.GetStub().GetState(reserveID)
	if err != nil {
		return fmt.Errorf("failed to read reserve from world state: %v", err)
	}
	if reserveJSON == nil {
		return fmt.Errorf("reserve %s does not exist", reserveID)
	}

	var reserve Reserve
	err = json.Unmarshal(reserveJSON, &reserve)
	if err != nil {
		return err
	}

	// Parse amounts
	allocateAmount, err := strconv.ParseFloat(amount, 64)
	if err != nil {
		return fmt.Errorf("invalid amount: %v", err)
	}

	availableAmount, err := strconv.ParseFloat(reserve.AvailableAmount, 64)
	if err != nil {
		return fmt.Errorf("invalid available amount: %v", err)
	}

	if allocateAmount > availableAmount {
		return fmt.Errorf("insufficient available reserve: requested %f, available %f", allocateAmount, availableAmount)
	}

	// Update reserve
	backedAmount, err := strconv.ParseFloat(reserve.BackedAmount, 64)
	if err != nil {
		return fmt.Errorf("invalid backed amount: %v", err)
	}

	reserve.BackedAmount = fmt.Sprintf("%.2f", backedAmount+allocateAmount)
	reserve.AvailableAmount = fmt.Sprintf("%.2f", availableAmount-allocateAmount)
	reserve.UpdatedAt = time.Now().Format(time.RFC3339)

	updatedJSON, err := json.Marshal(reserve)
	if err != nil {
		return err
	}

	err = ctx.GetStub().PutState(reserveID, updatedJSON)
	if err != nil {
		return fmt.Errorf("failed to update reserve in world state: %v", err)
	}

	return nil
}

// GetReserve returns reserve details
func (s *ReserveManagerContract) GetReserve(ctx contractapi.TransactionContextInterface, reserveID string) (*Reserve, error) {
	reserveJSON, err := ctx.GetStub().GetState(reserveID)
	if err != nil {
		return nil, fmt.Errorf("failed to read from world state: %v", err)
	}
	if reserveJSON == nil {
		return nil, fmt.Errorf("reserve %s does not exist", reserveID)
	}

	var reserve Reserve
	err = json.Unmarshal(reserveJSON, &reserve)
	if err != nil {
		return nil, err
	}

	return &reserve, nil
}

// Enforce1To1Backing verifies 1:1 backing ratio
func (s *ReserveManagerContract) Enforce1To1Backing(ctx contractapi.TransactionContextInterface, reserveID string) (bool, error) {
	reserve, err := s.GetReserve(ctx, reserveID)
	if err != nil {
		return false, err
	}

	totalAmount, err := strconv.ParseFloat(reserve.TotalAmount, 64)
	if err != nil {
		return false, err
	}

	backedAmount, err := strconv.ParseFloat(reserve.BackedAmount, 64)
	if err != nil {
		return false, err
	}

	// Check if total reserve >= backed amount (1:1 ratio)
	return totalAmount >= backedAmount, nil
}

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

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