import hashlib
import json
import time
from datetime import datetime
import database

class Blockchain:
    def __init__(self):
        self.chain = []
        # No guardamos la cadena en memoria permanentemente, la leemos de DB
        # Pero podemos mantener el último bloque para acceso rápido

    def create_genesis_block(self):
        """Crea el bloque génesis si no existe."""
        conn = database.get_db_connection()
        count = conn.execute("SELECT COUNT(*) FROM blockchain").fetchone()[0]
        if count == 0:
            genesis_block = {
                "index": 0,
                "timestamp": datetime.now().isoformat(),
                "transactions": json.dumps([{"sender": "SYSTEM", "recipient": "Numa_Fundador", "amount": 1000, "memo": "Genesis Block"}]),
                "previous_hash": "0",
                "hash": "" # Se calculará
            }
            genesis_block["hash"] = self.calculate_hash(genesis_block)
            self.save_block(conn, genesis_block)
            print("[Blockchain] Bloque Génesis creado.")
        conn.close()

    def calculate_hash(self, block):
        """Calcula el hash SHA-256 de un bloque."""
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

    def create_new_block(self, transactions):
        """Crea y guarda un nuevo bloque con las transacciones dadas."""
        last_block = self.get_last_block()
        if not last_block:
            self.create_genesis_block()
            last_block = self.get_last_block()

        new_block = {
            "index": last_block["index"] + 1,
            "timestamp": datetime.now().isoformat(),
            "transactions": json.dumps(transactions),
            "previous_hash": last_block["hash"],
            "hash": ""
        }
        new_block["hash"] = self.calculate_hash(new_block)
        
        conn = database.get_db_connection()
        self.save_block(conn, new_block)
        conn.close()
        return new_block

    def save_block(self, conn, block):
        """Guarda un bloque en la base de datos."""
        conn.execute('''
            INSERT INTO blockchain (block_index, timestamp, transactions, previous_hash, hash)
            VALUES (?, ?, ?, ?, ?)
        ''', (block["index"], block["timestamp"], block["transactions"], block["previous_hash"], block["hash"]))
        conn.commit()

    def get_last_block(self):
        """Obtiene el último bloque de la BD."""
        conn = database.get_db_connection()
        row = conn.execute("SELECT * FROM blockchain ORDER BY block_index DESC LIMIT 1").fetchone()
        conn.close()
        if row:
            d = dict(row)
            d["index"] = d.pop("block_index")
            return d
        return None

    def get_chain(self):
        """Devuelve toda la cadena."""
        conn = database.get_db_connection()
        rows = conn.execute("SELECT * FROM blockchain ORDER BY block_index ASC").fetchall()
        conn.close()
        chain = []
        for row in rows:
            d = dict(row)
            d["index"] = d.pop("block_index")
            chain.append(d)
        return chain

    def is_chain_valid(self):
        """Verifica la integridad de la cadena."""
        chain = self.get_chain()
        for i in range(1, len(chain)):
            current = chain[i]
            previous = chain[i - 1]

            # 1. Verificar hash del bloque actual
            # Reconstruimos el objeto bloque sin el hash para recalcularlo
            block_to_hash = {
                "index": current["index"],
                "timestamp": current["timestamp"],
                "transactions": current["transactions"],
                "previous_hash": current["previous_hash"],
                "hash": "" # Placeholder para el calculo igual que en create_new_block si usamos el mismo dict
                # Espera, mi calculate_hash usa el dict entero. 
                # Necesito reproducir EXACTAMENTE como se calculó.
                # En create_new_block:
                # new_block = { ... "hash": "" } -> calculate_hash -> new_block["hash"] = result
                # Así que debo poner hash="" temporalmente.
            }
            # Comentario: json.dumps ordenado por claves es crucial.
            # En create_new_block lo hice así:
            temp_block = {
                "index": current["index"],
                "timestamp": current["timestamp"],
                "transactions": current["transactions"],
                "previous_hash": current["previous_hash"],
                "hash": "" 
            }
            
            if current["hash"] != self.calculate_hash(temp_block):
                print(f"[Blockchain] Hash inválido en bloque {current['block_index']}")
                return False

            # 2. Verificar enlace con el anterior
            if current["previous_hash"] != previous["hash"]:
                print(f"[Blockchain] Enlace roto entre {previous['block_index']} y {current['block_index']}")
                return False

        return True

# Instancia global
blockchain_manager = Blockchain()
