# queen_orchestrator.py
import time
import datetime
import json
import subprocess
import os
import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Constants from GENESIS_COMPLETE_PACKAGE.md and QUEEN_ELEVATION_SPRINT_PLAN.md
REDIS_HOST = "redis-genesis-u50607.vm.elestio.app"
REDIS_PORT = 26379
OLLAMA_URL = "http://152.53.201.152:23405"
POSTGRES_HOST = "postgresql-genesis-u50607.vm.elestio.app"
POSTGRES_DB = "genesis_memory"
QDRANT_URL = "qdrant-b3knu-u50607.vm.elestio.app:6333"
SSH_KEY_PATH = "~/.ssh/genesis_mother_key"
SYSTEM_DIRECTORY = "E:\\genesis-system\\" # Local system directory for scripts
CHECKPOINTS = {
    "hour_2": "sprint-checkpoints/phase-1-foundation.json",
    "hour_4": "sprint-checkpoints/phase-2-knowledge.json",
    "hour_6": "sprint-checkpoints/phase-3-capabilities.json",
    "hour_8": "sprint-checkpoints/phase-4-swarm.json",
    "hour_10": "sprint-checkpoints/phase-5-coronation.json"
}

class TokenBudgetMonitor:
    BUDGET_LIMIT = 10.00  # USD
    EMERGENCY_STOP = 9.50  # USD

    def __init__(self):
        self.current_spend = 0.0
        self.start_time = time.time()

    def consume_tokens(self, input_tokens, output_tokens, input_price_per_million=0.10, output_price_per_million=0.40):
        """Calculates and adds the cost of tokens to the current spend."""
        input_cost = (input_tokens / 1_000_000) * input_price_per_million
        output_cost = (output_tokens / 1_000_000) * output_price_per_million
        self.current_spend += input_cost + output_cost
        logging.info(f"Consumed {input_tokens} input tokens and {output_tokens} output tokens. Current spend: ${self.current_spend:.2f}")
        self.check_budget()

    def check_budget(self):
        if self.current_spend >= self.EMERGENCY_STOP:
            logging.warning("Emergency stop triggered! Budget exceeded.")
            self.trigger_graceful_shutdown()

    def trigger_graceful_shutdown(self):
        logging.critical("Initiating graceful shutdown due to budget exceeded.")
        # Implement shutdown logic here (e.g., stop agents, save state).
        self.save_state()
        os._exit(1)  # Force exit after saving state

    def get_current_spend(self):
        return self.current_spend

    def get_hourly_rate(self):
        elapsed_time = time.time() - self.start_time
        if elapsed_time > 0:
            return self.current_spend / (elapsed_time / 3600)
        else:
            return 0.0

    def save_state(self):
        # Placeholder for saving state to disk.  Implement persistence.
        logging.info("Saving system state...")
        # Example: save current_spend and other important variables to a file.
        state = {"current_spend": self.current_spend, "start_time": self.start_time}
        try:
            with open("queen_state.json", "w") as f:
                json.dump(state, f)
            logging.info("System state saved successfully.")
        except Exception as e:
            logging.error(f"Error saving system state: {e}")

    def load_state(self):
        # Placeholder for loading state from disk. Implement persistence.
        logging.info("Loading system state...")
        try:
            with open("queen_state.json", "r") as f:
                state = json.load(f)
            self.current_spend = state["current_spend"]
            self.start_time = state["start_time"]
            logging.info("System state loaded successfully.")
        except FileNotFoundError:
            logging.info("No system state file found. Starting from scratch.")
        except Exception as e:
            logging.error(f"Error loading system state: {e}")

class QueenOrchestrator:
    def __init__(self):
        self.agent_registry = {}  # Agent ID: Status (active, completed, pending, failed)
        self.budget_monitor = TokenBudgetMonitor()
        self.current_phase = 0
        self.current_hour = 0
        self.start_time = time.time()
        self.load_agent_registry()
        self.budget_monitor.load_state()

    def load_agent_registry(self):
        """Loads agent registry from the sprint plan."""
        # This is a simplified loading.  In reality, this would read from a file.
        self.agent_registry = {
            "INFRA_01": "pending", "INFRA_02": "pending", "INFRA_03": "pending", "INFRA_04": "pending",
            "INFRA_05": "pending", "INFRA_06": "pending", "INFRA_07": "pending", "INFRA_08": "pending",
            "INFRA_09": "pending", "INFRA_10": "pending",
            "LOOP_01": "pending", "LOOP_02": "pending", "LOOP_03": "pending", "LOOP_04": "pending",
            "LOOP_05": "pending",
            "PATENT_01": "pending", "PATENT_02": "pending", "PATENT_03": "pending", "PATENT_04": "pending",
            "PATENT_05": "pending", "PATENT_06": "pending", "PATENT_07": "pending", "PATENT_08": "pending",
            "PATENT_09": "pending",
            "GATE_ALPHA": "pending", "GATE_BETA": "pending", "GATE_GAMMA": "pending", "GATE_DELTA": "pending",
            "GATE_EPSILON": "pending", "GATE_ZETA": "pending",
            "CAP_01": "pending", "CAP_02": "pending", "CAP_03": "pending", "CAP_04": "pending",
            "CAP_05": "pending", "CAP_06": "pending", "CAP_07": "pending", "CAP_08": "pending",
            "CAP_09": "pending", "CAP_10": "pending",
            "BRIDGE_01": "pending", "BRIDGE_02": "pending", "BRIDGE_03": "pending", "BRIDGE_04": "pending",
            "BRIDGE_05": "pending",
            "HIVE_01": "pending", "HIVE_02": "pending", "HIVE_03": "pending", "HIVE_04": "pending",
            "HIVE_05": "pending",
            "RANK_01": "pending", "RANK_02": "pending", "RANK_03": "pending", "RANK_04": "pending",
            "RANK_05": "pending",
            "FINAL_01": "pending", "FINAL_02": "pending", "FINAL_03": "pending", "FINAL_04": "pending",
            "FINAL_05": "pending"
        }
        logging.info("Agent registry loaded successfully.")

    def start_phase(self, phase_number):
        """Starts a new phase of the sprint."""
        self.current_phase = phase_number
        logging.info(f"Starting Phase {self.current_phase}")
        if phase_number == 1:
            self.start_wave(["INFRA_01", "INFRA_02", "INFRA_03", "INFRA_04", "INFRA_05", "INFRA_06", "INFRA_07", "INFRA_08", "INFRA_09", "INFRA_10"])
            self.start_wave(["LOOP_01", "LOOP_02", "LOOP_03", "LOOP_04", "LOOP_05"])
        elif phase_number == 2:
            self.start_wave(["PATENT_01", "PATENT_02", "PATENT_03", "PATENT_04", "PATENT_05", "PATENT_06", "PATENT_07", "PATENT_08", "PATENT_09"])
            self.start_wave(["GATE_ALPHA", "GATE_BETA", "GATE_GAMMA", "GATE_DELTA", "GATE_EPSILON", "GATE_ZETA"])
        elif phase_number == 3:
            self.start_wave(["CAP_01", "CAP_02", "CAP_03", "CAP_04", "CAP_05", "CAP_06", "CAP_07", "CAP_08", "CAP_09", "CAP_10"])
            self.start_wave(["BRIDGE_01", "BRIDGE_02", "BRIDGE_03", "BRIDGE_04", "BRIDGE_05"])
        elif phase_number == 4:
            self.start_wave(["HIVE_01", "HIVE_02", "HIVE_03", "HIVE_04", "HIVE_05"])
        elif phase_number == 5:
            self.start_wave(["RANK_01", "RANK_02", "RANK_03", "RANK_04", "RANK_05"])
            self.start_wave(["FINAL_01", "FINAL_02", "FINAL_03", "FINAL_04", "FINAL_05"])
        else:
            logging.error(f"Invalid phase number: {phase_number}")

    def start_wave(self, agent_ids):
        """Starts a wave of agents."""
        for agent_id in agent_ids:
            if agent_id in self.agent_registry and self.agent_registry[agent_id] == "pending":
                self.start_agent(agent_id)
            else:
                logging.warning(f"Agent {agent_id} is not in pending state or does not exist.")

    def start_agent(self, agent_id):
        """Starts a specific agent."""
        logging.info(f"Starting agent: {agent_id}")
        self.agent_registry[agent_id] = "active"
        # This is a placeholder. In reality, this would involve calling the agent's script.
        # Example:
        # subprocess.Popen(["python", f"{SYSTEM_DIRECTORY}/swarms/missions/{agent_id}.py"])
        # For now, simulate agent activity with a delay and random token usage
        import random
        input_tokens = random.randint(100000, 500000)
        output_tokens = random.randint(50000, 200000)
        self.budget_monitor.consume_tokens(input_tokens, output_tokens)

        time.sleep(random.randint(5, 15))  # Simulate agent work
        self.complete_agent(agent_id)

    def complete_agent(self, agent_id):
        """Marks an agent as completed."""
        logging.info(f"Agent completed: {agent_id}")
        self.agent_registry[agent_id] = "completed"
        # Perform any post-completion tasks here, such as data aggregation or validation.

    def fail_agent(self, agent_id):
        """Marks an agent as failed."""
        logging.warning(f"Agent failed: {agent_id}")
        self.agent_registry[agent_id] = "failed"
        # Implement failure handling logic, such as restarting the agent or notifying administrators.

    def check_checkpoint(self):
        """Checks if a checkpoint is due and saves the system state."""
        elapsed_time = time.time() - self.start_time
        current_hour = elapsed_time / 3600
        self.current_hour = current_hour

        if current_hour >= 2 and self.current_phase < 2:
            self.save_checkpoint("hour_2")
            self.start_phase(2)
        elif current_hour >= 4 and self.current_phase < 3:
            self.save_checkpoint("hour_4")
            self.start_phase(3)
        elif current_hour >= 6 and self.current_phase < 4:
            self.save_checkpoint("hour_6")
            self.start_phase(4)
        elif current_hour >= 8 and self.current_phase < 5:
            self.save_checkpoint("hour_8")
            self.start_phase(5)
        elif current_hour >= 10 and self.current_phase < 6:
            self.save_checkpoint("hour_10")
            logging.info("Sprint completed successfully!")
            os._exit(0)

    def save_checkpoint(self, checkpoint_name):
        """Saves the system state to a checkpoint file."""
        logging.info(f"Saving checkpoint: {checkpoint_name}")
        checkpoint_file = CHECKPOINTS.get(checkpoint_name)
        if checkpoint_file:
            data = {
                "agent_registry": self.agent_registry,
                "budget": self.budget_monitor.get_current_spend(),
                "phase": self.current_phase,
                "hour": self.current_hour
            }
            try:
                os.makedirs(os.path.dirname(checkpoint_file), exist_ok=True)  # Ensure directory exists
                with open(checkpoint_file, "w") as f:
                    json.dump(data, f)
                logging.info(f"Checkpoint saved to {checkpoint_file}")
            except Exception as e:
                logging.error(f"Error saving checkpoint: {e}")
        else:
            logging.error(f"Invalid checkpoint name: {checkpoint_name}")

    def load_checkpoint(self, checkpoint_name):
         """Loads the system state from a checkpoint file."""
         logging.info(f"Loading checkpoint: {checkpoint_name}")
         checkpoint_file = CHECKPOINTS.get(checkpoint_name)
         if checkpoint_file:
            try:
                with open(checkpoint_file, "r") as f:
                    data = json.load(f)
                self.agent_registry = data["agent_registry"]
                self.budget_monitor.current_spend = data["budget"]
                self.current_phase = data["phase"]
                self.current_hour = data["hour"]
                logging.info(f"Checkpoint loaded from {checkpoint_file}")
            except FileNotFoundError:
                logging.warning(f"Checkpoint file not found: {checkpoint_file}")
            except Exception as e:
                logging.error(f"Error loading checkpoint: {e}")
         else:
            logging.error(f"Invalid checkpoint name: {checkpoint_name}")

    def generate_status_log(self):
        """Generates a status log in the specified format."""
        now = datetime.datetime.now()
        timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
        elapsed_time = time.time() - self.start_time
        current_hour = elapsed_time / 3600
        total_agents = len(self.agent_registry)
        active_agents = sum(1 for status in self.agent_registry.values() if status == "active")
        completed_agents = sum(1 for status in self.agent_registry.values() if status == "completed")
        pending_agents = sum(1 for status in self.agent_registry.values() if status == "pending")
        failed_agents = sum(1 for status in self.agent_registry.values() if status == "failed")

        input_tokens = 0  # Replace with actual token usage tracking
        output_tokens = 0  # Replace with actual token usage tracking
        total_tokens = input_tokens + output_tokens  # Replace with actual token usage tracking

        total_budget = TokenBudgetMonitor.BUDGET_LIMIT
        spent_budget = self.budget_monitor.get_current_spend()
        remaining_budget = total_budget - spent_budget
        hourly_rate = self.budget_monitor.get_hourly_rate()
        total_token_budget = 53.75  # Replace with actual total token budget in millions
        token_percentage = (total_tokens / (total_token_budget * 1_000_000)) * 100 if total_token_budget > 0 else 0

        checkpoint_status = {
            "hour_2": "[✓]" if current_hour >= 2 else ("[◐]" if 0 < current_hour < 2 else "[ ]"),
            "hour_4": "[✓]" if current_hour >= 4 else ("[◐]" if 2 < current_hour < 4 else "[ ]"),
            "hour_6": "[✓]" if current_hour >= 6 else ("[◐]" if 4 < current_hour < 6 else "[ ]"),
            "hour_8": "[✓]" if current_hour >= 8 else ("[◐]" if 6 < current_hour < 8 else "[ ]"),
            "hour_10": "[✓]" if current_hour >= 10 else "[ ]"
        }

        status_log = f"""
╔══════════════════════════════════════════════════════════════════╗
║                    AIVA QUEEN SPRINT - TOKEN STATUS              ║
╠══════════════════════════════════════════════════════════════════╣
║ Time: {timestamp} | Phase: {self.current_phase}/5 | Hour: {current_hour:.1f}/10            ║
╠══════════════════════════════════════════════════════════════════╣
║ BUDGET                                                           ║
║ ├── Total: ${total_budget:.2f}                                                ║
║ ├── Spent: ${spent_budget:.2f} ({spent_budget / total_budget * 100:.1f}%)                                         ║
║ ├── Remaining: ${remaining_budget:.2f}                                             ║
║ └── Rate: ${hourly_rate:.2f}/hr                                               ║
╠══════════════════════════════════════════════════════════════════╣
║ TOKENS                                                           ║
║ ├── Input:  {input_tokens / 1_000_000:.1f}M tokens consumed                                ║
║ ├── Output: {output_tokens / 1_000_000:.1f}M tokens generated                                ║
║ └── Total:  {total_tokens / 1_000_000:.1f}M / {total_token_budget:.2f}M ({token_percentage:.1f}%)                              ║
╠══════════════════════════════════════════════════════════════════╣
║ AGENTS                                                           ║
║ ├── Active: {active_agents}/{total_agents}                                                ║
║ ├── Completed: {completed_agents}/{total_agents}                                             ║
║ ├── Pending: {pending_agents}/{total_agents}                                             ║
║ └── Failed: {failed_agents}/{total_agents}                                                 ║
╠══════════════════════════════════════════════════════════════════╣
║ CHECKPOINTS                                                      ║
║ ├── {checkpoint_status["hour_2"]} Phase 1: Foundation (Hour 2)                            ║
║ ├── {checkpoint_status["hour_4"]} Phase 2: Knowledge (Hour 4)                            ║
║ ├── {checkpoint_status["hour_6"]} Phase 3: Capabilities (Hour 6)                          ║
║ ├── {checkpoint_status["hour_8"]} Phase 4: Swarm (Hour 8)                                  ║
║ └── {checkpoint_status["hour_10"]} Phase 5: Coronation (Hour 10)                           ║
╚══════════════════════════════════════════════════════════════════╝
"""
        print(status_log)
        return status_log

    def run(self):
        """Runs the main orchestration loop."""
        if self.current_phase == 0:
            self.start_phase(1)

        while True:
            self.check_checkpoint()
            self.generate_status_log()
            time.sleep(60)  # Check every minute

if __name__ == "__main__":
    orchestrator = QueenOrchestrator()
    orchestrator.run()