# Standard library imports
import os

# Third party imports
import flask
import flask_cors  # type: ignore[import-untyped]
import opengeode
import geode_conversion
from opengeodeweb_microservice.schemas import get_schemas_dict
from opengeodeweb_back import geode_functions, utils_functions
from opengeodeweb_back.geode_objects.geode_brep import GeodeBRep
from opengeodeweb_back.geode_objects.geode_polygonal_surface3d import (
    GeodePolygonalSurface3D,
)

# Local application imports
from . import schemas

schemas_dict = get_schemas_dict(os.path.join(os.path.dirname(__file__), "schemas"))

routes = flask.Blueprint("create_routes", __name__)
flask_cors.CORS(routes)


@routes.route(schemas_dict["aoi"]["route"], methods=schemas_dict["aoi"]["methods"])
def create_aoi() -> flask.Response:
    """Endpoint to create an Area of Interest (AOI) as an PolygonalSurface3D"""
    utils_functions.validate_request(flask.request, schemas_dict["aoi"])
    params = schemas.Aoi.from_dict(flask.request.get_json())

    polygonal_surface = GeodePolygonalSurface3D()
    builder = polygonal_surface.builder()
    builder.set_name(params.name)

    for point in params.points:
        builder.create_point(opengeode.Point3D([point.x, point.y, params.z]))

    num_vertices = len(params.points)
    builder.create_polygon(list(range(num_vertices)))

    result = utils_functions.generate_native_viewable_and_light_viewable_from_object(
        polygonal_surface
    )
    return flask.make_response(result, 200)


@routes.route(schemas_dict["voi"]["route"], methods=schemas_dict["voi"]["methods"])
def create_voi() -> flask.Response:
    """Endpoint to create a Volume of Interest (VOI) as a BRep"""
    utils_functions.validate_request(flask.request, schemas_dict["voi"])
    params = schemas.Voi.from_dict(flask.request.get_json())

    aoi_data = geode_functions.get_data_info(params.aoi_id)
    if not aoi_data:
        flask.abort(404, f"AOI with id {params.aoi_id} not found")

    aoi_object = geode_functions.load_geode_object(params.aoi_id)
    if not isinstance(aoi_object, GeodePolygonalSurface3D):
        flask.abort(
            400, f"AOI with id {params.aoi_id} is not a GeodePolygonalSurface3D"
        )

    aoi_mesh = aoi_object.polygonal_surface
    nb_vertices = aoi_mesh.nb_vertices()

    bottom = [
        opengeode.Point3D(
            [aoi_mesh.point(i).value(0), aoi_mesh.point(i).value(1), params.z_min]
        )
        for i in range(nb_vertices)
    ]
    top = [
        opengeode.Point3D(
            [aoi_mesh.point(i).value(0), aoi_mesh.point(i).value(1), params.z_max]
        )
        for i in range(nb_vertices)
    ]

    prism_faces = [
        list(reversed(bottom)),
        list(top),
        *[
            [
                bottom[i],
                bottom[(i + 1) % nb_vertices],
                top[(i + 1) % nb_vertices],
                top[i],
            ]
            for i in range(nb_vertices)
        ],
    ]

    triangulated_surfaces: list[opengeode.SurfaceMesh3D] = []
    for face_points in prism_faces:
        polygonal_surface = opengeode.PolygonalSurface3D.create()
        surface_builder = opengeode.PolygonalSurfaceBuilder3D.create(polygonal_surface)
        for point in face_points:
            surface_builder.create_point(point)
        surface_builder.create_polygon(list(range(len(face_points))))
        opengeode.triangulate_surface_mesh3D(polygonal_surface)
        converted = opengeode.convert_surface_mesh_into_triangulated_surface3D(
            polygonal_surface
        )
        if converted is None:
            flask.abort(500, "Failed to triangulate one or more VOI faces")
        triangulated_surfaces.append(converted)

    brep = geode_conversion.convert_meshes_into_brep([], [], triangulated_surfaces)
    geode_brep = GeodeBRep(brep)
    geode_brep.builder().set_name(params.name)

    result = utils_functions.generate_native_viewable_and_light_viewable_from_object(
        geode_brep
    )
    return flask.make_response(result, 200)
