from qiskit import QuantumCircuit, Aer, execute
from qiskit.utils import QuantumInstance
import math
import numpy as np
from qiskit.circuit.library import QFT

def near_practical_ecdsa_attack(public_point, generator_point, order, a, p):
    """
    Highly conceptual demonstration of a quantum ECDLP solver.

    EXTREME WARNING: This code is still theoretical and DOES NOT break real ECDSA.
    It represents a "near-practical" approach only in terms of increased
    complexity, but it's far from a real attack.
    """

    def qft_dagger(qc, n):
        """n-qubit QFTdagger the first n qubits in circ."""
        for qubit in range(n // 2):
            qc.swap(qubit, n - qubit - 1)
        for j in range(n):
            for m in range(j):
                qc.cp(-np.pi / float(2**(j - m)), m, j)
            qc.h(j)

    def controlled_point_addition(qc, control_qubits, point_qubits, generator_point, a, p):
        """
        Simulates controlled point addition with more realistic modular addition.
        Still highly simplified compared to a real quantum circuit.
        """
        n_qubits_point = len(point_qubits) // 2
        generator_x = bin(generator_point[0])[2:].zfill(n_qubits_point)
        generator_y = bin(generator_point[1])[2:].zfill(n_qubits_point)

        for i in range(n_qubits_point):
            if generator_x[n_qubits_point - 1 - i] == '1':
                for control_qubit in control_qubits:
                    #Simulated modular addition using ccx gates.
                    qc.mcx([control_qubit, point_qubits[i]], point_qubits[i]+n_qubits_point*2)
                    qc.cx(point_qubits[i]+n_qubits_point*2,point_qubits[i])
                    qc.mcx([control_qubit, point_qubits[i]], point_qubits[i]+n_qubits_point*2)

            if generator_y[n_qubits_point - 1 - i] == '1':
                for control_qubit in control_qubits:
                    qc.mcx([control_qubit, point_qubits[i + n_qubits_point]], point_qubits[i]+n_qubits_point*2)
                    qc.cx(point_qubits[i]+n_qubits_point*2,point_qubits[i + n_qubits_point])
                    qc.mcx([control_qubit, point_qubits[i + n_qubits_point]], point_qubits[i]+n_qubits_point*2)

    n_qubits_order = int(math.ceil(math.log2(order)))
    n_qubits_point = int(math.ceil(math.log2(p)))

    qc = QuantumCircuit(n_qubits_order + 2 * n_qubits_point + n_qubits_point *2, n_qubits_order)

    generator_x_bin = bin(generator_point[0])[2:].zfill(n_qubits_point)
    generator_y_bin = bin(generator_point[1])[2:].zfill(n_qubits_point)

    for i in range(n_qubits_point):
        if generator_x_bin[n_qubits_point - 1 - i] == '1':
            qc.x(n_qubits_order + i)
        if generator_y_bin[n_qubits_point - 1 - i] == '1':
            qc.x(n_qubits_order + n_qubits_point + i)

    qc.h(range(n_qubits_order))

    for i in range(n_qubits_order):
        controlled_point_addition(qc, [i], range(n_qubits_order, n_qubits_order + 2 * n_qubits_point), generator_point, a, p)

    qft_dagger(qc, n_qubits_order)

    qc.measure(range(n_qubits_order), range(n_qubits_order))

    backend = Aer.get_backend('qasm_simulator')
    quantum_instance = QuantumInstance(backend, shots=1024)
    result = quantum_instance.execute(qc)
    counts = result.get_counts()

    max_count = 0
    max_value = 0
    for value, count in counts.items():
        if count > max_count:
            max_count = count
            max_value = int(value, 2)

    estimated_private_key = max_value

    print(f"Estimated Private Key (highly conceptual): {estimated_private_key}")
    print("WARNING: The output is a highly unreliable estimate. Due to the lack of proper elliptic curve operations, the simulated private key does NOT reliably match the actual private key. This is a purely theoretical demonstration and does NOT break real ECDSA.")