Matrix centric approach
Code to place a lamp object at center of each selected face of a mesh, with light local -Z axis pointing in direction of face normal.
Find the global direction of a face normal by premultiplying with the objects global rotation matrix. (No scale).
import bpy
import bmesh
context = bpy.context
ob = context.edit_object
mw = ob.matrix_world
me = ob.data
bm = bmesh.from_edit_mesh(me)
faces = [f for f in bm.faces if f.select]
while faces:
f = faces.pop()
light = bpy.data.lights.new(
f"Face{f.index}",
type='AREA',
)
light.size = 1
light_ob = bpy.data.objects.new(
f"Face{f.index}",
light,
)
M = mw.normalized() @ f.normal.to_track_quat('-Z', 'Y').to_matrix().to_4x4()
M.translation = mw @ f.calc_center_median()
light_ob.matrix_world = M
context.collection.objects.link(light_ob)
Local with parenting.
The above code adds the lights in the scene at the global orientation of the face. If the mesh is moved the lights do not go along with it.
Another option would be to use only local coordinates and make each light a child of the mesh.
Scaling to fit the face.
It appears you are also scaling the area light to fit the face. To do this when not axis aligned is a bit more in depth than finding axes minima / maxima. IMO quite possibly warrants another question
Some examples that use another vector to align. The face has some calc tangent methods, based on longest edge, edges. Can adjust by rotating light about its local z axis (the face normal) to match the tangent. This way have one known axis aligned with an edge and can scale to match.
https://blender.stackexchange.com/a/94047/15543
Align a face normal to Z axis.
https://blender.stackexchange.com/a/121227/15543
similarly in this one the poly interior angles maps the corner verts to 2d. Similarly here if the face normal is aligned to an axis the coords can be mapped to 2d.
https://blender.stackexchange.com/a/203355/15543
If the coords are in 2d there are some helper methods (primarily for UV) that fit rectangles.
Using mathutils.geometry.box_fit_2d