import numpy as np
from steady_pinball_theory.contract import CanonicalContract,wall_velocity
from steady_pinball_theory.metrics import profile_errors
from steady_pinball_theory.theory import solve_mfs


def test_canonical_geometry_and_reynolds():
 c=CanonicalContract(); assert c.centers==((1000.0,299.5),(1026.0,314.5),(1026.0,284.5)); assert c.viscosity==0.004

def test_source_level_wall_oracle():
 c=CanonicalContract(); o=c.oracle(5.0)
 assert o['plus-minus']['action']==[0.0,0.005,-0.005]
 assert o['plus-minus']['rear_y_plus_outer_top'][0] < 0
 assert o['plus-minus']['rear_y_plus_gap_bottom'][0] > 0
 assert o['minus-plus']['rear_y_plus_outer_top'][0] > 0
 assert wall_velocity(2,3,4)==(-8.0,6.0)

def test_primary_metric_is_pointwise_vector_norm():
 r=profile_errors(np.array([1.,.8]),np.array([0.,.1]),u_inf=1.)
 assert np.isclose(r['E_inf_vector'],np.sqrt(.05))

def test_unbounded_mfs_impermeability_and_zero_source():
 centers=np.array([[0.,0.],[1.3,.75],[1.3,-.75]])
 sol,diag=solve_mfs(centers,.5,[0.,2.,-2.],n_boundary=48)
 assert diag['boundary_Linf'] < 2e-5
 for q in sol.strengths.reshape(3,-1): assert abs(q.sum()) < 1e-10

def test_strip_images_reduce_wall_normal_velocity():
 centers=np.array([[0.,0.],[1.3,.75],[1.3,-.75]])
 sol,diag=solve_mfs(centers,.5,[0.,2.,-2.],n_boundary=48,H=15.,image_layers=20)
 x=np.linspace(-5,8,300); p=np.r_[np.c_[x,np.full_like(x,15.)],np.c_[x,np.full_like(x,-15.)]]
 assert np.max(np.abs(sol.velocity(p)[:,1])) < 2e-3
 assert diag['boundary_Linf'] < 3e-5


def test_mfs_resolution_and_source_radius_sensitivity():
 centers=np.array([[0.,0.],[1.3,.75],[1.3,-.75]])
 rows=[]
 for n in (32,48,64):
  for ratio in (.55,.65,.75):
   _,d=solve_mfs(centers,.5,[0.,2.,-2.],n_boundary=n,source_ratio=ratio)
   rows.append(d['boundary_Linf'])
 assert max(rows) < 2e-4

def test_strip_image_layer_sensitivity():
 centers=np.array([[0.,0.],[1.3,.75],[1.3,-.75]])
 vals=[]
 for layers in (10,20,40):
  sol,d=solve_mfs(centers,.5,[0.,2.,-2.],n_boundary=48,H=15.,image_layers=layers)
  vals.append(sol.velocity(np.array([[10.,0.]]))[0])
  assert d['boundary_Linf'] < 4e-5
 assert np.max(np.linalg.norm(np.diff(vals,axis=0),axis=1)) < 2e-4
