#!/usr/bin/env python3
"""
Script per estrarre chiavi pubbliche P2PK con UTXO non spesi
dal database SQLite generato dallo scanner
"""

import sqlite3
import sys
import os

def extract_p2pk_unspent(db_path, output_file):
    """
    Estrae le chiavi pubbliche P2PK non spese dal database
    e le salva in un file di testo (una per riga)
    """

    # Verifica esistenza database
    if not os.path.exists(db_path):
        print(f"[ERROR] Database non trovato: {db_path}")
        print("[!] Esegui prima lo scanner Python per creare il database")
        return False

    try:
        # Connetti al database
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()

        # Query per ottenere solo P2PK non spesi
        query = """
        SELECT
            scriptpubkey,
            block_height,
            txid,
            output_index,
            value_satoshi
        FROM p2pk_addresses
        WHERE is_unspent = 1
        ORDER BY value_satoshi DESC
        """

        cursor.execute(query)
        results = cursor.fetchall()

        if not results:
            print("[!] Nessun P2PK non speso trovato nel database")
            conn.close()
            return False

        print(f"[+] Trovati {len(results)} P2PK non spesi")
        print("[+] Estrazione in corso...")

        # Estrai le chiavi pubbliche
        pubkeys = []
        total_value = 0

        with open(output_file, 'w') as f:
            # Header
            f.write("# Bitcoin P2PK Public Keys (Unspent)\n")
            f.write("# Format: One public key per line (hex, uncompressed)\n")
            f.write("# Generated from database\n")
            f.write("#\n")

            for row in results:
                scriptpubkey, block_height, txid, output_index, value_sat = row

                # Il scriptpubkey è nel formato: 41<pubkey>ac o 21<pubkey>ac
                # Dobbiamo estrarre solo la pubkey

                # Rimuovi il primo byte (41 o 21) e l'ultimo byte (ac - OP_CHECKSIG)
                if len(scriptpubkey) == 134:  # Non compresso: 41 + 130 char (65 bytes) + ac
                    pubkey = scriptpubkey[2:-2]  # Rimuovi 41 e ac
                elif len(scriptpubkey) == 70:  # Compresso: 21 + 66 char (33 bytes) + ac
                    pubkey = scriptpubkey[2:-2]  # Rimuovi 21 e ac
                    # NOTA: Il bruteforce genera solo pubkey non compresse
                    # quindi le chiavi compresse non verranno trovate
                    print(f"[!] SKIP chiave compressa: {txid}:{output_index}")
                    continue
                else:
                    print(f"[!] SKIP formato sconosciuto: {scriptpubkey}")
                    continue

                # Aggiungi il prefisso 04 se non c'è (formato non compresso)
                if not pubkey.startswith('04'):
                    pubkey = '04' + pubkey

                # Verifica lunghezza corretta (130 caratteri hex = 65 bytes)
                if len(pubkey) != 130:
                    print(f"[!] SKIP lunghezza errata ({len(pubkey)}): {txid}:{output_index}")
                    continue

                # Scrivi la pubkey nel file
                f.write(f"{pubkey}\n")
                pubkeys.append(pubkey)
                total_value += value_sat

                # Info dettagliate
                btc_value = value_sat / 100000000.0
                print(f"  [{len(pubkeys)}] Block {block_height} | {btc_value:.8f} BTC | {txid[:16]}...:{output_index}")

        conn.close()

        # Statistiche finali
        print("\n" + "="*60)
        print("  ESTRAZIONE COMPLETATA")
        print("="*60)
        print(f"Chiavi pubbliche estratte: {len(pubkeys)}")
        print(f"Valore totale: {total_value / 100000000.0:.8f} BTC")
        print(f"Valore totale: {total_value:,} satoshi")
        print(f"File output: {output_file}")
        print("="*60)

        return True

    except sqlite3.Error as e:
        print(f"[ERROR] Errore database: {e}")
        return False
    except Exception as e:
        print(f"[ERROR] Errore: {e}")
        return False


def show_stats(db_path):
    """
    Mostra statistiche sui P2PK nel database
    """

    if not os.path.exists(db_path):
        print(f"[ERROR] Database non trovato: {db_path}")
        return

    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()

        # Statistiche generali
        cursor.execute("SELECT COUNT(*) FROM p2pk_addresses")
        total_p2pk = cursor.fetchone()[0]

        cursor.execute("SELECT COUNT(*) FROM p2pk_addresses WHERE is_unspent = 1")
        unspent_p2pk = cursor.fetchone()[0]

        cursor.execute("SELECT SUM(value_satoshi) FROM p2pk_addresses")
        total_value = cursor.fetchone()[0] or 0

        cursor.execute("SELECT SUM(value_satoshi) FROM p2pk_addresses WHERE is_unspent = 1")
        unspent_value = cursor.fetchone()[0] or 0

        print("\n" + "="*60)
        print("  STATISTICHE DATABASE P2PK")
        print("="*60)
        print(f"Totale P2PK:        {total_p2pk}")
        print(f"P2PK non spesi:     {unspent_p2pk}")
        print(f"P2PK spesi:         {total_p2pk - unspent_p2pk}")
        print(f"-" * 60)
        print(f"Valore totale:      {total_value / 100000000.0:.8f} BTC")
        print(f"Valore non speso:   {unspent_value / 100000000.0:.8f} BTC")
        print(f"Valore speso:       {(total_value - unspent_value) / 100000000.0:.8f} BTC")
        print("="*60)

        # Top 10 P2PK non spesi per valore
        print("\nTop 10 P2PK non spesi per valore:")
        print("-" * 60)

        cursor.execute("""
        SELECT block_height, txid, output_index, value_satoshi, scriptpubkey
        FROM p2pk_addresses
        WHERE is_unspent = 1
        ORDER BY value_satoshi DESC
        LIMIT 10
        """)

        for i, row in enumerate(cursor.fetchall(), 1):
            block, txid, vout, value, script = row
            btc = value / 100000000.0
            print(f"{i:2d}. Block {block:7d} | {btc:12.8f} BTC | {txid[:16]}...:{vout}")

        print("="*60 + "\n")

        conn.close()

    except sqlite3.Error as e:
        print(f"[ERROR] Errore database: {e}")


def main():
    # Percorsi di default
    db_path = "../databases/bitcoin_p2pk_study.db"
    output_file = "target_keys.txt"

    print("="*60)
    print("  Bitcoin P2PK UTXO Extractor")
    print("  Estrae chiavi pubbliche non spese per bruteforce")
    print("="*60)
    print()

    # Controlla argomenti
    if len(sys.argv) > 1:
        if sys.argv[1] in ['-h', '--help']:
            print("Uso:")
            print(f"  {sys.argv[0]} [database.db] [output.txt]")
            print()
            print("Esempi:")
            print(f"  {sys.argv[0]}")
            print(f"  {sys.argv[0]} custom.db keys.txt")
            print(f"  {sys.argv[0]} --stats")
            return
        elif sys.argv[1] == '--stats':
            show_stats(db_path)
            return
        else:
            db_path = sys.argv[1]

    if len(sys.argv) > 2:
        output_file = sys.argv[2]

    # Mostra statistiche prima dell'estrazione
    show_stats(db_path)

    # Conferma utente
    print(f"[?] Database: {db_path}")
    print(f"[?] Output:   {output_file}")
    print()
    response = input("Procedere con l'estrazione? (s/n): ")

    if response.lower() not in ['s', 'y', 'si', 'yes']:
        print("[!] Operazione annullata")
        return

    # Estrai le chiavi
    print()
    if extract_p2pk_unspent(db_path, output_file):
        print()
        print("[+] Estrazione completata con successo!")
        print(f"[+] Ora puoi eseguire: ./p2pk_bruteforce {output_file}")
    else:
        print()
        print("[!] Estrazione fallita")
        sys.exit(1)


if __name__ == "__main__":
    main()
