package bitcoin

import (
	"fmt"

	"btcphrasechecker/types"

	"github.com/btcsuite/btcd/btcutil"
	"github.com/btcsuite/btcd/btcutil/hdkeychain"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
)

// DerivationPath represents a BIP derivation path configuration
type DerivationPath struct {
	Name    string
	Path    string
	Purpose uint32
	Desc    string
}

// Standard Bitcoin derivation paths
var StandardPaths = []DerivationPath{
	{
		Name:    "BIP44 (Legacy)",
		Path:    "m/44'/0'/0'/0",
		Purpose: 44,
		Desc:    "P2PKH addresses (1...)",
	},
	{
		Name:    "BIP49 (SegWit)",
		Path:    "m/49'/0'/0'/0",
		Purpose: 49,
		Desc:    "P2SH-wrapped SegWit (3...)",
	},
	{
		Name:    "BIP84 (Native SegWit)",
		Path:    "m/84'/0'/0'/0",
		Purpose: 84,
		Desc:    "Bech32 addresses (bc1...)",
	},
}

// DeriveAddresses derives Bitcoin addresses from a seed for all standard paths
func DeriveAddresses(seed []byte, addressCount int) ([]types.AddressInfo, error) {
	masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
	if err != nil {
		return nil, fmt.Errorf("failed to create master key: %w", err)
	}

	var allAddresses []types.AddressInfo

	for _, pathInfo := range StandardPaths {
		addresses, err := derivePathAddresses(masterKey, pathInfo, addressCount)
		if err != nil {
			return nil, err
		}
		allAddresses = append(allAddresses, addresses...)
	}

	return allAddresses, nil
}

// derivePathAddresses derives addresses for a specific derivation path
func derivePathAddresses(masterKey *hdkeychain.ExtendedKey, pathInfo DerivationPath, count int) ([]types.AddressInfo, error) {
	var addresses []types.AddressInfo

	// Derive base path: m/purpose'/coin_type'/account'/change
	// For Bitcoin: coin_type = 0, account = 0, change = 0 (external addresses)
	key := masterKey
	key, _ = key.Derive(hdkeychain.HardenedKeyStart + pathInfo.Purpose)
	key, _ = key.Derive(hdkeychain.HardenedKeyStart + 0) // coin_type = 0 for Bitcoin
	key, _ = key.Derive(hdkeychain.HardenedKeyStart + 0) // account = 0
	key, _ = key.Derive(0)                               // change = 0 (external)

	// Derive individual addresses
	for i := uint32(0); i < uint32(count); i++ {
		childKey, err := key.Derive(i)
		if err != nil {
			continue
		}

		pubKey, err := childKey.ECPubKey()
		if err != nil {
			continue
		}

		pubKeyBytes := pubKey.SerializeCompressed()
		address, err := deriveAddress(pubKeyBytes, pathInfo.Purpose)
		if err != nil {
			continue
		}

		addresses = append(addresses, types.AddressInfo{
			Address: address,
			Path:    fmt.Sprintf("%s/%d", pathInfo.Path, i),
			Chain:   types.ChainBitcoin,
		})
	}

	return addresses, nil
}

// deriveAddress creates an address from a public key based on the purpose
func deriveAddress(pubKeyBytes []byte, purpose uint32) (string, error) {

	switch purpose {
	case 44:
		// Legacy P2PKH
		addr, err := btcutil.NewAddressPubKey(pubKeyBytes, &chaincfg.MainNetParams)
		if err != nil {
			return "", err
		}
		return addr.EncodeAddress(), nil

	case 49:
		// P2SH-wrapped SegWit (BIP49)
		// Create witness program for P2WPKH
		pubKeyHash := btcutil.Hash160(pubKeyBytes)
		witnessProgram, err := txscript.NewScriptBuilder().
			AddOp(txscript.OP_0).
			AddData(pubKeyHash).
			Script()
		if err != nil {
			return "", err
		}
		// Wrap witness program in P2SH
		scriptAddr, err := btcutil.NewAddressScriptHash(witnessProgram, &chaincfg.MainNetParams)
		if err != nil {
			return "", err
		}
		return scriptAddr.EncodeAddress(), nil

	case 84:
		// Native SegWit (bech32)
		addr, err := btcutil.NewAddressWitnessPubKeyHash(
			btcutil.Hash160(pubKeyBytes),
			&chaincfg.MainNetParams,
		)
		if err != nil {
			return "", err
		}
		return addr.EncodeAddress(), nil

	default:
		return "", fmt.Errorf("unsupported purpose: %d", purpose)
	}
}
