import numpy as np
from typing import Tuple


class FieldSimulation:
    """
    Field Theory Extension (ρ_s / φ).
    ∇² φ = α ( ρ_s - <ρ_s> )
    Simulates salience density across a task-space grid.
    """

    def __init__(self, grid_size: int = 20, alpha: float = 0.5):
        self.grid_size = grid_size
        self.alpha = alpha
        self.rho = np.zeros((grid_size, grid_size))
        self.phi = np.zeros((grid_size, grid_size))

    def add_salience(self, x: int, y: int, amount: float):
        self.rho[x, y] += amount

    def step_field(self):
        """
        Solve Poisson equation via relaxation method.
        φ_new = (φ_neighbors - α * (ρ - <ρ>)) / 4
        """
        rho_mean = np.mean(self.rho)
        new_phi = self.phi.copy()

        for x in range(1, self.grid_size - 1):
            for y in range(1, self.grid_size - 1):
                neighbors = (
                    self.phi[x + 1, y]
                    + self.phi[x - 1, y]
                    + self.phi[x, y + 1]
                    + self.phi[x, y - 1]
                )
                source = self.alpha * (self.rho[x, y] - rho_mean)
                new_phi[x, y] = (neighbors - source) / 4.0

        self.phi = new_phi

    def get_gradient(self, x: int, y: int) -> Tuple[float, float]:
        """Movement is dictated by ∇φ (attraction to salience peaks)."""
        x = max(1, min(x, self.grid_size - 2))
        y = max(1, min(y, self.grid_size - 2))
        gx = self.phi[x + 1, y] - self.phi[x - 1, y]
        gy = self.phi[x, y + 1] - self.phi[x, y - 1]
        return float(gx), float(gy)

    def get_empirical_signatures(self) -> str:
        return (
            "1. Domain Clumping: Salience should cluster in task-subsets.\n"
            "2. Potential Wells: High-salience areas should trap agents (stalling).\n"
            "3. Continuity Strains: Large ∇φ between adjacent steps predicts errors."
        )
