#!/usr/bin/env python3
"""
fib_scheduler.py

Elevated from R&D (shotgun_flowtorch_brain_expander.py).

Technical function:
- CPU affinity binding using Fibonacci sequence for port distribution
- Golden ratio-based parallel processing layout

Notes:
- This is not "brain expansion", it's Fibonacci-based CPU scheduling.
- Integrated as OPTIONAL scheduler for parallel workloads.
"""

import os
import subprocess
from typing import List, Dict, Optional


# Fibonacci sequence for port distribution
FIBONACCI_PORTS = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]


def get_cpu_count() -> int:
    """Get number of available CPU cores"""
    return os.cpu_count() or 1


def generate_port_mapping(num_ports: Optional[int] = None) -> Dict[int, int]:
    """
    Generate mapping of Fibonacci ports to CPU cores.

    Args:
        num_ports: Number of ports to use (defaults to CPU count)

    Returns:
        Dict mapping port number to CPU core index
    """
    cpu_count = get_cpu_count()
    ports_to_use = num_ports or min(cpu_count, len(FIBONACCI_PORTS))

    mapping = {}
    for i in range(ports_to_use):
        port = FIBONACCI_PORTS[i]
        cpu_core = i % cpu_count
        mapping[port] = cpu_core

    return mapping


def assign_ports_to_cores(dry_run: bool = False) -> Dict[int, int]:
    """
    Assign Fibonacci ports to CPU cores.

    Args:
        dry_run: If True, return mapping without executing taskset

    Returns:
        Dict mapping port number to CPU core index
    """
    mapping = generate_port_mapping()

    if not dry_run:
        # This would require actual process management
        # For now, return mapping only
        pass

    return mapping


def bind_process_to_port(pid: int, port: int) -> bool:
    """
    Bind a process to a CPU core based on Fibonacci port.

    Args:
        pid: Process ID to bind
        port: Fibonacci port number (will be mapped to CPU core)

    Returns:
        True if successful, False otherwise
    """
    mapping = generate_port_mapping()

    if port not in mapping:
        return False

    cpu_core = mapping[port]

    try:
        subprocess.check_call(['taskset', '-cp', str(cpu_core), str(pid)])
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False


def get_optimal_port_for_task(task_id: int) -> int:
    """
    Get optimal Fibonacci port for a given task.

    Uses hash of task_id to distribute evenly.

    Args:
        task_id: Unique task identifier

    Returns:
        Fibonacci port number
    """
    mapping = generate_port_mapping()
    available_ports = list(mapping.keys())

    # Use modulo to distribute tasks evenly
    port_index = task_id % len(available_ports)
    return available_ports[port_index]


def format_distribution_report() -> str:
    """
    Generate human-readable report of port-to-core distribution.

    Returns:
        Formatted string showing the mapping
    """
    mapping = generate_port_mapping()
    cpu_count = get_cpu_count()

    report_lines = [
        f"Fibonacci Port Distribution ({len(mapping)} ports, {cpu_count} cores)",
        "=" * 60
    ]

    for port, cpu_core in sorted(mapping.items()):
        report_lines.append(f"  Port {port:03d} → CPU Core {cpu_core}")

    return "\n".join(report_lines)
