#!/usr/bin/env python3
"""
CryptoVault Installer + Mining v1.0
====================================
Single EXE installer with mining capability and cold wallet mining support
"""

import os
import sys
import struct
import hashlib
import zipfile
import shutil
import subprocess
import tempfile
import re
from pathlib import Path

# ==========================================
# CONSTANTS & HELPERS
# ==========================================
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'

def log(msg, color=OKGREEN):
    print(f"{color}[✓] {msg}{ENDC}")

def log_warn(msg):
    log(msg, WARNING)

def log_err(msg):
    log(msg, FAIL)

def sha256_file(path):
    h = hashlib.sha256()
    with open(path, 'rb') as f:
        for chunk in iter(lambda: f.read(8192), b''):
            h.update(chunk)
    return h.hexdigest()

def r(src, dst):
    """Force copytree"""
    if os.path.exists(dst):
        shutil.rmtree(dst)
    shutil.copytree(src, dst)

# ==========================================
# MINING ENGINE
# ==========================================
class MiningEngine:
    """CPU-only mining for cold wallet accumulation"""
    
    SUPPORTED_ALGOS = {
        'ETH': 'ethash',      # Ethereum
        'ETC': 'ethash',      # Ethereum Classic
        'XMR': 'randomx',     # Monero
        'ZEC': 'equihash',    # Zcash
        'RVN': ' KawPow',     # Ravencoin
        'AE': 'cuckoo',       # Aeternity
    }
    
    @staticmethod
    def get_miner_binary(coin):
        """Return bundled miner URL or None"""
        miners = {
            'ETH': 'https://github.com/trexminer/T-Rex/releases/download/0.25.12/t-rex-0.25.12-win.zip',
            'XMR': 'https://github.com/xmrig/xmrig/releases/download/v6.22.2/xmrig-6.22.2-win64.zip',
        }
        return miners.get(coin.upper())

# ==========================================
# WINDOWS LAUNCHER (.exe stub)
# ==========================================
class ExeLauncher:
    """Builds the single-file CryptoVault.exe"""
    
    def __init__(self, version="1.0.0"):
        self.version = version
    
    def build(self, source_dir, output_path):
        """Build standalone .exe from source"""
        wallet_html = os.path.join(source_dir, 'wallet-windows', 'index.html')
        if not os.path.exists(wallet_html):
            raise FileNotFoundError("wallet-windows/index.html not found")
        
        exe_code = self._generate_exe()
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(exe_code)
        
        # Make executable
        os.chmod(output_path, 0o755)
        log(f"Built: {output_path}")
        return output_path
    
    def _generate_exe(self):
        return r'''#!/usr/bin/env python3
"""
CryptoVault Single EXE
======================
All-in-one wallet + mining launcher
"""
import sys, os, hashlib, json, struct, ctypes, subprocess, tempfile, zipfile, time
from pathlib import Path

# ==========================================
# CONFIG
# ==========================================
VERSION = "1.0.0"
WALLET_DATA = {}
COIN = "ETH"
POOL = ""
WALLET_ADDR = ""
RIG_ID = f"cryptovacault-{os.getenv('COMPUTERNAME','pc')}"
THREADS = os.cpu_count() or 4
MAX_FAILS = 5

# ==========================================
# COLORS
# ==========================================
C = type('C', (), {
    'HEADER': '\033[95m', 'OKBLUE': '\033[94m', 'OKGREEN': '\033[92m',
    'WARNING': '\033[93m', 'FAIL': '\033[91m', 'ENDC': '\033[0m',
    'BOLD': '\033[1m'
})()

def log(m=""):
    print(f"{C.OKGREEN}[*] {m}{C.ENDC}")

def log_err(m=""):
    print(f"{C.FAIL}[!] {m}{C.ENDC}")

def log_warn(m=""):
    print(f"{C.WARNING}[~] {m}{C.ENDC}")

# ==========================================
# PASSWORD / VAULT
# ==========================================
def set_password():
    import getpass
    p1 = getpass.getpass("Set vault password: ")
    if len(p1) < 8:
        log_err("Password must be at least 8 characters")
        return set_password()
    p2 = getpass.getpass("Confirm password: ")
    if p1 != p2:
        log_err("Passwords don't match")
        return set_password()
    salt = os.urandom(32)
    import hashlib
    key = hashlib.pbkdf2_hmac('sha512', p1.encode(), salt, 100000)
    with open(vault_file(), 'w') as f:
        json.dump({'salt': salt.hex(), 'key': key.hex(), 'addrs': {}}, f)
    log("Password set! Vault created.")

def vault_file():
    return os.path.join(os.environ.get('APPDATA','.'), 'CryptoVault', 'vault.dat')

def unlock():
    import getpass
    if not os.path.exists(vault_file()):
        log_warn("No vault found. Setting up new vault...")
        set_password()
        return True
    with open(vault_file()) as f:
        v = json.load(f)
    for attempt in range(MAX_FAILS):
        p = getpass.getpass("Enter vault password: ")
        import hashlib
        key = hashlib.pbkdf2_hmac('sha512', p.encode(), bytes.fromhex(v['salt']), 100000)
        if key.hex() == v['key']:
            log("Vault unlocked!")
            return True
        log_err(f"Wrong password ({attempt+1}/{MAX_FAILS})")
    log_err("Too many failed attempts. Wiping vault...")
    os.remove(vault_file())
    return False

def change_password():
    import getpass
    with open(vault_file()) as f:
        v = json.load(f)
    p = getpass.getpass("Current password: ")
    import hashlib
    key = hashlib.pbkdf2_hmac('sha512', p.encode(), bytes.fromhex(v['salt']), 100000)
    if key.hex() != v['key']:
        log_err("Incorrect current password")
        return
    p1 = getpass.getpass("New password: ")
    if len(p1) < 8:
        log_err("Password must be at least 8 characters")
        return
    p2 = getpass.getpass("Confirm new password: ")
    if p1 != p2:
        log_err("Passwords don't match")
        return
    import hashlib
    salt = os.urandom(32)
    new_key = hashlib.pbkdf2_hmac('sha512', p1.encode(), salt, 100000)
    v = {'salt': salt.hex(), 'key': new_key.hex(), 'addrs': v.get('addrs', {})}
    with open(vault_file(), 'w') as f:
        json.dump(v, f, indent=2)
    log("Password changed!")

# ==========================================
# WALLET ADDRESSES
# ==========================================
def add_wallet():
    addr = input("Wallet address (0x...): ").strip()
    if not addr.startswith('0x') and not addr.startswith('x'):
        log_err("Invalid address format")
        return
    coin = input("Coin (BTC/ETH/SOL/XMR/RVN): ").strip().upper() or "ETH"
    with open(vault_file()) as f:
        v = json.load(f)
    v['addrs'][coin] = addr
    with open(vault_file(), 'w') as f:
        json.dump(v, f, indent=2)
    log(f"Added {coin} wallet: {addr[:10]}...")

def list_wallets():
    with open(vault_file()) as f:
        v = json.load(f)
    addrs = v.get('addrs', {})
    if not addrs:
        log_warn("No wallets saved")
        return
    print(f"\\n{BOLD}Saved Wallets:{ENDC}")
    for coin, addr in addrs.items():
        print(f"  {coin}: {addr}")

# ==========================================
# MINING
# ==========================================
MINER_PID = None

def start_mining():
    global MINER_PID
    with open(vault_file()) as f:
        v = json.load(f)
    addrs = v.get('addrs', {})
    
    coin = input(f"Coin to mine ({'/'.join(addrs.keys()) or 'ETH'}): ").strip().upper() or list(addrs.keys())[0]
    if coin not in addrs:
        log_err(f"No {coin} wallet found. Add one first.")
        return
    
    wallet = addrs[coin]
    algo_map = {'ETH': 'ethash', 'ETC': 'ethash', 'XMR': 'randomx', 'ZEC': 'equihash', 'RVN': 'kawpow'}
    algo = algo_map.get(coin, 'ethash')
    
    pools = {
        'ETH': f"stratum+tcp://eth.coinfoundry.org:3443",
        'XMR': f"stratum+tcp://pool.hashvault.pro:3333",
    }
    pool = pools.get(coin, f"stratum+tcp://{coin}.mine.example.com:3333")
    
    print(f"\\n{BOLD}Starting {coin} mining → {wallet[:10]}...{ENDC}")
    print(f"  Pool: {pool}")
    print(f"  Threads: {THREADS}")
    print(f"  Algo: {algo}")
    print("  Press Ctrl+C to stop\\n")
    
    miner_url = {
        'ETH': 'https://github.com/trexminer/T-Rex/releases/download/0.25.12/t-rex-0.25.12-win.zip',
        'XMR': 'https://github.com/xmrig/xmrig/releases/download/v6.22.2/xmrig-6.22.2-win64.zip',
    }
    
    tmpdir = tempfile.mkdtemp()
    try:
        if coin in miner_url:
            log(f"Downloading miner for {coin}...")
            zip_path = os.path.join(tmpdir, 'miner.zip')
            subprocess.run(['curl', '-L', '-o', zip_path, miner_url[coin]], check=True, timeout=60)
            with zipfile.ZipFile(zip_path, 'r') as z:
                z.extractall(tmpdir)
            exe = os.path.join(tmpdir, [f for f in os.listdir(tmpdir) if f.endswith('.exe')][0])
        else:
            log_err(f"No miner available for {coin}. Mining engine not yet built.")
            return
        
        cmd = [exe, '-a', algo, '-o', pool, '-u', wallet, '-p', f'x={RIG_ID}', '-t', str(THREADS)]
        if coin == 'ETH':
            cmd += ['--algo', 'ethash', '--fee', '0.5']
        
        subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        log(f"Mining started (PID: {MINER_PID})")
    except Exception as e:
        log_err(f"Mining failed: {e}")
    finally:
        input("Press Enter to return...")

def stop_mining():
    global MINER_PID
    if MINER_PID:
        try:
            os.kill(MINER_PID, 9)
            log("Mining stopped")
        except:
            pass
        MINER_PID = None

# ==========================================
# MINING DASHBOARD
# ==========================================
def mining_dashboard():
    """Simple live mining dashboard"""
    global MINER_PID
    print(f"\\n{BOLD}{'='*50}")
    print(f"  CryptoVault Mining Dashboard")
    print(f"{'='*50}{ENDC}")
    print()
    
    with open(vault_file()) as f:
        v = json.load(f)
    addrs = v.get('addrs', {})
    
    if not addrs:
        log_warn("No wallets configured. Add a wallet first.")
        return
    
    print(f"Configured wallets: {len(addrs)}")
    for c, a in addrs.items():
        print(f"  {c}: {a[:10]}...")
    print()
    
    if MINER_PID:
        log(f"Mining active (PID: {MINER_PID})")
        print("  [S] Stop mining")
    else:
        log("Mining not started")
        print("  [M] Start mining")
    
    print("  [A] Add wallet")
    print("  [L] List wallets")
    print("  [R] Remove wallet")
    print("  [P] Change password")
    print("  [Q] Quit")
    print()

# ==========================================
# MAIN MENU
# ==========================================
def main():
    global MINER_PID
    print(f"\\n{BOLD}╔═══════════════════════════════════════╗")
    print(f"║   CryptoVault v{VERSION:<25}║")
    print(f"║   Bootable Multi-Coin Wallet + Miner      ║")
    print(f"╚═══════════════════════════════════════╝{ENDC}\\n")
    
    if not os.path.exists(vault_file()):
        log_warn("First time setup!")
        set_password()
    
    if not unlock():
        sys.exit(1)
    
    while True:
        mining_dashboard()
        choice = input(f"{BOLD}Choice>{ENDC} ").strip().upper()
        
        if choice == 'M':
            start_mining()
        elif choice == 'S':
            stop_mining()
        elif choice == 'A':
            add_wallet()
        elif choice == 'L':
            list_wallets()
        elif choice == 'R':
            rm_coin = input("Coin to remove: ").strip().upper()
            with open(vault_file()) as f:
                v = json.load(f)
            if rm_coin in v['addrs']:
                del v['addrs'][rm_coin]
                with open(vault_file(), 'w') as f:
                    json.dump(v, f, indent=2)
                log(f"Removed {rm_coin}")
            else:
                log_err(f"No {rm_coin} wallet found")
        elif choice == 'P':
            change_password()
        elif choice == 'Q':
            stop_mining()
            print("Goodbye!")
            sys.exit(0)

if __name__ == '__main__':
    main()
''' + '\n' * 1000

# ==========================================
# SINGLE-FILE BUNDLER
# ==========================================
class SingleFileBundler:
    """Bundles everything into one .exe"""
    
    def __init__(self):
        self.tmp = tempfile.mkdtemp()
        self.output = "./output/CryptoVault.exe"
    
    def build(self):
        log("Building single-file CryptoVault.exe...")
        
        src = "./output"
        wallet_html = os.path.join(src, "wallet-windows", "index.html")
        shell_reg = os.path.join(src, "wallet-windows", "shell-integration.reg")
        autorun_inf = os.path.join(src, "autorun.inf")
        shamir = os.path.join(src, "backup", "shamir_backup.py")
        
        for f in [wallet_html, shell_reg, autorun_inf, shamir]:
            if not os.path.exists(f):
                raise FileNotFoundError(f"Missing: {f}")
        
        # Build launcher
        launcher = ExeLauncher()
        exe_path = os.path.join(self.tmp, "cryptovacault.exe")
        launcher.build(src, exe_path)
        
        # Create .bat launcher (more compatible on Windows)
        bat_path = os.path.join(self.tmp, "CryptoVault.bat")
        bat_code = f'''@echo off
title CryptoVault Wallet v1.0
cd /d %~dp0
python "%~dp0cryptovacault.exe" %*
'''
        with open(bat_path, 'w') as f:
            f.write(bat_code)
        
        # Package everything
        os.makedirs(os.path.dirname(self.output), exist_ok=True)
        zip_path = self.output.replace('.exe', '.zip')
        
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
            zf.write(exe_path, 'cryptovacault.exe')
            for root, dirs, files in os.walk(src):
                for file in files:
                    fp = os.path.join(root, file)
                    arcname = os.path.relpath(fp, os.path.dirname(src))
                    if not file.endswith('.exe'):
                        zf.write(fp, arcname)
        
        log(f"Single-file bundle: {zip_path}")
        shutil.rmtree(self.tmp)
        return zip_path

# ==========================================
# MAIN
# ==========================================
def main():
    print(f"{BOLD}╔═══════════════════════════════════════╗")
    print(f"║   CryptoVault Single-EXE Builder v1.0  ║")
    print(f"╚═══════════════════════════════════════╝{ENDC}\\n")
    
    builder = SingleFileBundler()
    output = builder.build()
    
    print(f"\\n{OKGREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{ENDC}")
    print(f"{BOLD}Build complete!{ENDC}")
    print(f"  {output}")
    print(f"\\nExtract and run CryptoVault.bat on Windows")
    print(f"Features: password vault, wallet manager, start mining")
    print(f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{ENDC}")

if __name__ == '__main__':
    main()
