import bpy

scene = bpy.context.scene
ZW = -0.18
bpy.ops.mesh.primitive_plane_add(size=64, location=(0,0,ZW))
wat = bpy.context.active_object
wat.name = "PondWater"
for p in wat.data.polygons: p.use_smooth=True

def link_to(obj, cname):
    col = bpy.data.collections.get(cname)
    if col is None:
        col = bpy.data.collections.new(cname); scene.collection.children.link(col)
    for c in list(obj.users_collection): c.objects.unlink(obj)
    col.objects.link(obj)
link_to(wat, "Water")

mat = bpy.data.materials.new("WaterMat"); mat.use_nodes=True
nt = mat.node_tree; nt.nodes.clear()
out = nt.nodes.new('ShaderNodeOutputMaterial'); out.location=(700,0)
absorb = nt.nodes.new('ShaderNodeVolumeAbsorption'); absorb.location=(460,-300)
absorb.inputs['Color'].default_value=(0.10,0.28,0.20,1)
absorb.inputs['Density'].default_value=0.85
mixsh = nt.nodes.new('ShaderNodeMixShader'); mixsh.location=(520,40)
nt.links.new(mixsh.outputs['Shader'], out.inputs['Surface'])

glass = nt.nodes.new('ShaderNodeBsdfGlass'); glass.location=(160,140)
glass.inputs['Roughness'].default_value=0.02
glass.inputs['IOR'].default_value=1.333
diff  = nt.nodes.new('ShaderNodeBsdfDiffuse'); diff.location=(160,-30)
diff.inputs['Color'].default_value=(0.012,0.030,0.024,1)
transp= nt.nodes.new('ShaderNodeBsdfTransparent'); transp.location=(160,-170)

add1 = nt.nodes.new('ShaderNodeAddShader'); add1.location=(330,80)
nt.links.new(glass.outputs['BSDF'], add1.inputs[0])
nt.links.new(diff.outputs['BSDF'], add1.inputs[1])
foamf = nt.nodes.new('ShaderNodeBsdfDiffuse'); foamf.location=(160,-300)
foamf.inputs['Color'].default_value=(0.85,0.88,0.86,1)
foamf.inputs['Roughness'].default_value=0.75
add2 = nt.nodes.new('ShaderNodeAddShader'); add2.location=(430,-40)
nt.links.new(add1.outputs['Shader'], add2.inputs[0])
nt.links.new(foamf.outputs['BSDF'], add2.inputs[1])
nt.links.new(add2.outputs['Shader'], mixsh.inputs[1])
nt.links.new(transp.outputs['BSDF'], mixsh.inputs[2])

geo = nt.nodes.new('ShaderNodeNewGeometry'); geo.location=(-860,320)
objt = nt.nodes.new('ShaderNodeTexCoord'); objt.location=(-860,120)
sep = nt.nodes.new('ShaderNodeSeparateXYZ'); sep.location=(-680,320)
nt.links.new(geo.outputs['Position'], sep.inputs['Vector'])
mr1 = nt.nodes.new('ShaderNodeMapRange'); mr1.location=(-500,400)
mr1.inputs['From Min'].default_value=-0.34
mr1.inputs['From Max'].default_value=-0.13
mr1.clamp=True
nt.links.new(sep.outputs['Z'], mr1.inputs['Value'])

noiz = nt.nodes.new('ShaderNodeTexNoise'); noiz.location=(-680,580)
noiz.inputs['Scale'].default_value=3.2; noiz.inputs['Detail'].default_value=4
noiz.inputs['Distortion'].default_value=0.6
nt.links.new(objt.outputs['Object'], noiz.inputs['Vector'])
nsub = nt.nodes.new('ShaderNodeMath'); nsub.operation='SUBTRACT'; nsub.location=(-340,520)
nsub.inputs[1].default_value=0.42
nt.links.new(noiz.outputs['Fac'], nsub.inputs[0])
mul = nt.nodes.new('ShaderNodeMath'); mul.operation='MULTIPLY'; mul.location=(-160,450)
nt.links.new(mr1.outputs['Result'], mul.inputs[0])
nt.links.new(nsub.outputs['Value'], mul.inputs[1])
clampn = nt.nodes.new('ShaderNodeClamp'); clampn.location=(10,450)
clampn.inputs['Min'].default_value=0.0; clampn.inputs['Max'].default_value=1.0
nt.links.new(mul.outputs['Value'], clampn.inputs['Value'])
nt.links.new(clampn.outputs['Result'], mixsh.inputs['Fac'])

bmp = nt.nodes.new('ShaderNodeBump'); bmp.location=(-40,-120)
bmp.inputs['Strength'].default_value=0.06; bmp.inputs['Distance'].default_value=0.015
rip = nt.nodes.new('ShaderNodeTexNoise'); rip.location=(-360,-200)
rip.inputs['Scale'].default_value=9.0; rip.inputs['Detail'].default_value=6
rip.inputs['Distortion'].default_value=1.2
nt.links.new(objt.outputs['Object'], rip.inputs['Vector'])
nt.links.new(rip.outputs['Fac'], bmp.inputs['Height'])
for tgt in (glass, diff):
    nt.links.new(bmp.outputs['Normal'], tgt.inputs['Normal'])

wat.data.materials.append(mat)
result={"ok":True}