import hashlib
import time
from typing import List, Dict
import random
class Vote:
    def __init__(self, voter_id: str, national_candidate_id: str,
regional_candidate_id: str, timestamp: float = None):
        self.voter_id = voter_id
        self.national_candidate_id = national_candidate_id
        self.regional_candidate_id = regional_candidate_id
        self.timestamp = timestamp if timestamp else time.time()
    def __repr__(self):
        return f"Vote(voter_id={self.voter_id},
national_candidate_id={self.national_candidate_id},
regional_candidate_id={self.regional_candidate_id}, timestamp={self.timestamp})"
class Block:
    def __init__(self, index: int, votes: List[Vote], previous_hash: str,
timestamp: float = None):
        self.index = index
        self.votes = votes
        self.previous_hash = previous_hash
        self.timestamp = timestamp if timestamp else time.time()
        self.hash = self.calculate_hash()
    def calculate_hash(self) -> str:
        block_data = str(self.index) + str(self.votes) + self.previous_hash +
str(self.timestamp)
        return hashlib.sha256(block_data.encode()).hexdigest()
    def __repr__(self):
        return f"Block(index={self.index}, votes={self.votes},
previous_hash={self.previous_hash}, timestamp={self.timestamp}, hash={self.hash})"
class Blockchain:
    def __init__(self):
        self.chain = [self.create_genesis_block()]
        self.vote_history = {} # Store votes to prevent double-voting
        self.voter_registry: Dict[str, Dict[str, str]] = {} # Store voter details
        self.candidates: Dict[str, Dict[str, Dict[str, str]]] = {
            "national": {},
            "regional": {}
        } # Store candidate details
        self.regions: Dict[str, str] = {} # Store region details
        self.results = {
            "national": {},
            "regional": {}
        } # Running tally for vote counts
        self.election_phase = "registration" # Initialize election phase
        self.parties: Dict[str, str] = {} # Store party details
        self.regional_support: Dict[str, int] = {} # Tracks regional support per
party
    def create_genesis_block(self) -> Block:
        return Block(0, [], "0")
    def register_voter(self, name: str, region: str) -> str:
        if self.election_phase != "registration":
            raise ValueError("Voter registration is not allowed during this
phase.")
        voter_id = hashlib.sha256((name + region).encode()).hexdigest()
        self.voter_registry[voter_id] = {"name": name, "region": region}
        return voter_id
    def add_party(self, name: str) -> str:
        party_id = hashlib.sha256(name.encode()).hexdigest()
        self.parties[party_id] = name
        return party_id
    def add_candidate(self, name: str, party_id: str, candidate_type: str) -> str:
        if candidate_type not in ["national", "regional"]:
            raise ValueError("Invalid candidate type.")
        candidate_id = hashlib.sha256(name.encode()).hexdigest()
        self.candidates[candidate_type][candidate_id] = {"name": name, "party_id":
party_id}
        self.results[candidate_type][candidate_id] = 0 # Initialize vote count
        # Initialize the regional results for each region when a candidate is added
        if candidate_type == "regional":
            for region_id in self.results["regional"]:
                self.results["regional"][region_id][candidate_id] = 0
        return candidate_id
    def add_region(self, name: str) -> str:
        region_id = hashlib.sha256(name.encode()).hexdigest()
        self.regions[region_id] = name
        self.results["regional"][region_id] = {}
        # Initialize regional candidate vote counts for this region
        for candidate_id in self.candidates["regional"]:
            self.results["regional"][region_id][candidate_id] = 0
        return region_id
    def cast_vote(self, voter_id: str, national_candidate_id: str,
regional_candidate_id: str) -> str:
        if self.election_phase != "voting":
            raise ValueError("Voting is not allowed during this phase.")
        if voter_id not in self.voter_registry:
            raise ValueError("Voter is not registered.")
        region_id = self.voter_registry[voter_id]["region"]
        if voter_id in self.vote_history:
            raise ValueError("Voter has already voted.")
        vote = Vote(voter_id, national_candidate_id, regional_candidate_id)
        self.vote_history[voter_id] = vote
        # Tally the vote
        self.results["national"][national_candidate_id] += 1
        self.results["regional"][region_id][regional_candidate_id] += 1
        # Track regional support by party
        party_id = self.candidates["regional"][regional_candidate_id]["party_id"]
        if party_id not in self.regional_support:
            self.regional_support[party_id] = 0
        self.regional_support[party_id] += 1
        previous_block = self.chain[-1]
        new_block_index = previous_block.index + 1
          new_block = Block(new_block_index, [vote], previous_block.hash)
          self.chain.append(new_block)
        receipt = f"Vote Receipt:\nVoter ID: {voter_id}\nNational Candidate:
{self.candidates['national'][national_candidate_id]['name']}\nRegional Candidate:
{self.candidates['regional'][regional_candidate_id]['name']}"
        return receipt
    def display_results(self):
        if self.election_phase != "results":
            raise ValueError("Results are not available during this phase.")
          print("National Results:")
          for candidate_id, count in self.results["national"].items():
              print(f"{self.candidates['national'][candidate_id]['name']}: {count}
votes")
        print("\nRegional Results:")
        for region_id, region_results in self.results["regional"].items():
            print(f"Region: {self.regions[region_id]}")
            for candidate_id, count in region_results.items():
                print(f"{self.candidates['regional'][candidate_id]['name']}:
{count} votes")
        # Determine the national president by regional support
        winning_party_id = max(self.regional_support,
key=self.regional_support.get)
        winning_party_name = self.parties[winning_party_id]
        print(f"\nNational President's Party: {winning_party_name}")
    def start_voting(self):
        self.election_phase = "voting"
    def stop_voting(self):
        self.election_phase = "counting"
    def announce_results(self):
        self.election_phase = "results"
# Example usage
blockchain = Blockchain()
# Add parties
party1_id = blockchain.add_party("Party 1")
party2_id = blockchain.add_party("Party 2")
party3_id = blockchain.add_party("Party 3")
# Add national candidates
national_candidate1_id = blockchain.add_candidate("National Candidate 1",
party1_id, "national")
national_candidate2_id = blockchain.add_candidate("National Candidate 2",
party2_id, "national")
national_candidate3_id = blockchain.add_candidate("National Candidate 3",
party3_id, "national")
# Add regions
region1_id = blockchain.add_region("Region 1")
region2_id = blockchain.add_region("Region 2")
region3_id = blockchain.add_region("Region 3")
region4_id   =   blockchain.add_region("Region   4")
region5_id   =   blockchain.add_region("Region   5")
region6_id   =   blockchain.add_region("Region   6")
region7_id   =   blockchain.add_region("Region   7")
region8_id   =   blockchain.add_region("Region   8")
# Add regional candidates
regional_candidate1_id = blockchain.add_candidate("Regional Candidate 1",
party1_id, "regional")
regional_candidate2_id = blockchain.add_candidate("Regional Candidate 2",
party2_id, "regional")
regional_candidate3_id = blockchain.add_candidate("Regional Candidate 3",
party3_id, "regional")
# Additional regional candidates omitted for brevity...
# Register voters
for i in range(10000):
    region_id = random.choice(list(blockchain.regions.keys()))
    voter_id = blockchain.register_voter(f"Voter {i}", region_id)
# Start voting
blockchain.election_phase = "voting"
# Cast votes (simplified random assignment)
for voter_id in blockchain.voter_registry:
    region_id = blockchain.voter_registry[voter_id]["region"]
    regional_candidate_id = random.choice(list(blockchain.results["regional"]
[region_id].keys()))
    national_candidate_id =
random.choice(list(blockchain.results["national"].keys()))
    blockchain.cast_vote(voter_id, national_candidate_id, regional_candidate_id)
# Start results
blockchain.election_phase = "results"
# Display results
blockchain.display_results()