"""
Holographic physics and black hole thermodynamics.

Implements Bekenstein-Hawking entropy, information scrambling,
MSS chaos bound, and Ryu-Takayanagi entanglement entropy.

References:
    [1] Bekenstein, Phys. Rev. D7:2333 (1973)
    [2] Maldacena, Shenker, Stanford, JHEP 1608:106 (2016)
    [3] Susskind, arXiv:1403.5695 (2014)
    [4] Hayden & Preskill, JHEP 0709:120 (2007)
    [5] Ryu & Takayanagi, Phys. Rev. Lett. 96:181602 (2006)
"""

import numpy as np
from scipy.special import gamma
from typing import List, Dict, Tuple, Any


class HolographicSystem:
    """
    Holographic physics computations for navigation in chaotic spacetime.

    Connects to fundamental physics from black hole thermodynamics,
    information scrambling, and the AdS/CFT correspondence.
    """

    def __init__(self, dimensions: int = 11):
        self.dimensions = dimensions
        self.planck_length = 1.616e-35  # meters
        self.planck_time = 5.391e-44    # seconds
        self.information_capacity = self._calculate_information_capacity()
        self.scrambling_time = None

    def _calculate_information_capacity(self) -> float:
        """
        Maximum information capacity using holographic principle.

        I_max = A / (4 * l_p^2) bits
        """
        if self.dimensions <= 3:
            area = 4 * np.pi  # Unit sphere
        else:
            area = (
                2 * np.pi ** (self.dimensions / 2)
                / gamma(self.dimensions / 2)
            )
        return area / (4 * self.planck_length ** 2)

    def bekenstein_hawking_entropy(self, energy: float, size: float) -> float:
        """
        Bekenstein-Hawking entropy: S_BH = 2 pi M r_s.

        Args:
            energy: System energy (arbitrary units)
            size: System size (arbitrary units)

        Returns:
            Entropy in bits
        """
        r_s = 2 * energy / (size ** 2)
        entropy_bh = 2 * np.pi * energy * r_s
        return entropy_bh / np.log(2)

    def information_scrambling_time(
        self, system_size: int, temperature: float = 1.0
    ) -> float:
        """
        Fast scrambling time (Sekino-Susskind conjecture).

        t_scramble ~ (beta / 2 pi) log(N)

        Args:
            system_size: Number of degrees of freedom
            temperature: System temperature

        Returns:
            Scrambling time
        """
        beta = 1.0 / temperature
        t_scramble = (beta / (2 * np.pi)) * np.log(system_size)
        self.scrambling_time = t_scramble
        return t_scramble

    def maldacena_shenker_stanford_bound(
        self, lyapunov_exponent: float, temperature: float
    ) -> Tuple[bool, bool]:
        """
        Check the MSS chaos bound: lambda_L <= 2 pi T / hbar.

        Args:
            lyapunov_exponent: Measured Lyapunov exponent
            temperature: System temperature

        Returns:
            (satisfies_bound, is_maximally_chaotic)
        """
        mss_bound = 2 * np.pi * temperature
        satisfies = lyapunov_exponent <= mss_bound
        is_maximal = abs(lyapunov_exponent - mss_bound) / mss_bound < 0.1
        return satisfies, is_maximal

    def page_curve_time(self, initial_entropy: float) -> float:
        """
        Page time for information recovery: t_Page ~ S / (2 dS/dt).

        Args:
            initial_entropy: Initial entropy

        Returns:
            Page time
        """
        entropy_rate = (
            initial_entropy / self.scrambling_time
            if self.scrambling_time
            else 1.0
        )
        return initial_entropy / (2 * entropy_rate)

    def holographic_complexity(self, circuit_depth: int, gate_count: int) -> float:
        """
        Holographic complexity (CV conjecture): C = V / (G l).

        Args:
            circuit_depth: Quantum circuit depth
            gate_count: Number of quantum gates

        Returns:
            Holographic complexity
        """
        return circuit_depth * np.log(gate_count)

    def entanglement_entropy_ryu_takayanagi(
        self, region_size: float, total_size: float
    ) -> float:
        """
        Ryu-Takayanagi entanglement entropy: S_A = Area(gamma_A) / (4G).

        Args:
            region_size: Size of subsystem A
            total_size: Total system size

        Returns:
            Entanglement entropy
        """
        ratio = region_size / total_size
        if ratio < 0.5:
            ent = region_size ** (self.dimensions - 1)
        else:
            ent = (total_size - region_size) ** (self.dimensions - 1)
        area_law_coeff = 1.0 / (4 * self.dimensions)
        return area_law_coeff * ent

    def butterfly_effect_velocity(
        self, perturbation_size: float, time_evolution: List[float]
    ) -> float:
        """
        Butterfly velocity from OTOC decay.

        Args:
            perturbation_size: Initial perturbation strength
            time_evolution: Time series of perturbation growth

        Returns:
            Butterfly velocity
        """
        if len(time_evolution) < 10:
            return 0.0
        time_array = np.arange(len(time_evolution))
        log_growth = np.log(np.abs(time_evolution) + 1e-10)
        slope, _ = np.polyfit(time_array, log_growth, 1)
        return perturbation_size / slope if slope > 0 else float('inf')
