package bitcoin

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"btcphrasechecker/types"
)

const (
	// API base URL (using blockstream.info)
	apiBaseURL = "https://blockstream.info/api"
)

// BlockstreamTransaction represents a transaction from Blockstream API
type BlockstreamTransaction struct {
	Txid   string `json:"txid"`
	Status struct {
		Confirmed   bool   `json:"confirmed"`
		BlockHeight int    `json:"block_height"`
		BlockTime   int64  `json:"block_time"`
	} `json:"status"`
	Vin []struct {
		Txid    string `json:"txid"`
		Vout    int    `json:"vout"`
		Prevout struct {
			Scriptpubkey        string `json:"scriptpubkey"`
			ScriptpubkeyAddress string `json:"scriptpubkey_address"`
			Value               uint64 `json:"value"`
		} `json:"prevout"`
	} `json:"vin"`
	Vout []struct {
		Scriptpubkey        string `json:"scriptpubkey"`
		ScriptpubkeyAddress string `json:"scriptpubkey_address"`
		Value               uint64 `json:"value"`
	} `json:"vout"`
	Fee uint64 `json:"fee"`
}

// GetAddressInfo fetches address information from Blockstream API
func GetAddressInfo(address string) (balance, txCount uint64, received, sent uint64, err error) {
	url := fmt.Sprintf("%s/address/%s", apiBaseURL, address)

	resp, err := http.Get(url)
	if err != nil {
		return 0, 0, 0, 0, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return 0, 0, 0, 0, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, 0, 0, 0, err
	}

	var addrData struct {
		ChainStats struct {
			FundedTxoSum uint64 `json:"funded_txo_sum"`
			SpentTxoSum  uint64 `json:"spent_txo_sum"`
			TxCount      int    `json:"tx_count"`
		} `json:"chain_stats"`
	}

	if err := json.Unmarshal(body, &addrData); err != nil {
		return 0, 0, 0, 0, err
	}

	balance = addrData.ChainStats.FundedTxoSum - addrData.ChainStats.SpentTxoSum
	txCount = uint64(addrData.ChainStats.TxCount)
	received = addrData.ChainStats.FundedTxoSum
	sent = addrData.ChainStats.SpentTxoSum

	return balance, txCount, received, sent, nil
}

// GetTransactions fetches all transactions for an address
func GetTransactions(address string) ([]types.Transaction, error) {
	url := fmt.Sprintf("%s/address/%s/txs", apiBaseURL, address)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var bsTxs []BlockstreamTransaction
	if err := json.Unmarshal(body, &bsTxs); err != nil {
		return nil, err
	}

	// Convert to common transaction format
	var transactions []types.Transaction
	for _, bsTx := range bsTxs {
		tx := convertTransaction(bsTx, address)
		transactions = append(transactions, tx)
	}

	return transactions, nil
}

// convertTransaction converts Blockstream transaction to common format
func convertTransaction(bsTx BlockstreamTransaction, forAddress string) types.Transaction {
	tx := types.Transaction{
		TxID:        bsTx.Txid,
		BlockHeight: bsTx.Status.BlockHeight,
		Confirmed:   bsTx.Status.Confirmed,
		Fee:         bsTx.Fee,
	}

	if bsTx.Status.BlockTime > 0 {
		tx.Time = time.Unix(bsTx.Status.BlockTime, 0)
	}

	// Calculate received and sent amounts for this address
	var received, sent uint64
	addressSet := make(map[string]bool)

	// Check inputs (sent from address)
	for _, vin := range bsTx.Vin {
		if vin.Prevout.ScriptpubkeyAddress == forAddress {
			sent += vin.Prevout.Value
		}
		addressSet[vin.Prevout.ScriptpubkeyAddress] = true
	}

	// Check outputs (received by address)
	for _, vout := range bsTx.Vout {
		if vout.ScriptpubkeyAddress == forAddress {
			received += vout.Value
		}
		addressSet[vout.ScriptpubkeyAddress] = true
	}

	tx.Received = received
	tx.Sent = sent
	tx.NetChange = int64(received) - int64(sent)

	// Collect all unique addresses
	for addr := range addressSet {
		if addr != "" && addr != forAddress {
			tx.Addresses = append(tx.Addresses, addr)
		}
	}

	return tx
}
