from datetime import datetime
import json
from flask import Flask, jsonify, request, make_response, g
from main.models._mapped_tables import CustomPolygon as CustomPolygonModel
from main.models._mapped_tables import CustomGroup as CustomGroupModel
from main.models._mapped_tables import db_selector, PointAsc, PointDesc
from flask_restful import Resource
from setup import DB_SESSION, admin_required, CONFIG
from shapely.geometry import Polygon
from shapely import from_geojson
from shapely.errors import GEOSException
from geoalchemy2.shape import from_shape
from sqlalchemy import exc, select, update
from ..services.enums.user import PortalLayer
from main.services.access_helper import AccessHelper

from main.models._mapped_tables import PointAsc, PointDesc
from functools import wraps

class CustomPolygons(Resource):
    method_decorators = [admin_required]

    def get(self, database):
        with DB_SESSION[db_selector(database)].begin() as db_session:
            custom_polygons = db_session.query(CustomPolygonModel).all()

            output = {}
            for custom_polygon in custom_polygons:
                if not output.get(custom_polygon.custom_group.name):
                    output[custom_polygon.custom_group.name] = []
                output.get(custom_polygon.custom_group.name).append(custom_polygon.to_dict_minimal)

            return jsonify(output)

    def post(self, database):
        data = json.loads(request.data)

        try:
            geometry = from_geojson(json.dumps(data.get('geometry')))
            if geometry.geom_type != 'Polygon':
                return make_response(jsonify({'error': 'Geometry must be a Polygon'}), 406)
        except GEOSException:
            return make_response(jsonify({'error': 'Invalid geometry'}), 406)

        custom_polygon = CustomPolygonModel(
            custom_group_id=data.get('custom_group_id'),
            reference=data.get('reference'),
            geometry=geometry
        )
        if custom_polygon.validates is True:
            try:
                with DB_SESSION[db_selector(database)].begin() as db_session:
                    custom_polygon.metrics = custom_polygon.calculate_metrics(AccessHelper(), database)

                    db_session.add(custom_polygon)
                    db_session.commit()

                with DB_SESSION[db_selector(database)].begin() as db_session: # Re-retrieve custom_polygon in a new session because SQLAlchemy cannot do simple things.
                    custom_polygon = db_session.query(CustomPolygonModel).filter(CustomPolygonModel.id == custom_polygon.id).one_or_none()
                    output = custom_polygon.to_dict

                return make_response({"message": "Custom Polygon created"} | output, 201)
            except exc.IntegrityError:
                return make_response(jsonify({"error": "It was not possible to create the CustomPolygon."}), 400)
        else:
            return make_response(jsonify({"error": custom_polygon.validates}), 406)

class CustomPolygon(Resource):
    method_decorators = [admin_required]
    
    def get(self, database, custom_polygon_id):
        with DB_SESSION[db_selector(database)].begin() as db_session:
            custom_polygon = db_session.query(CustomPolygonModel).filter(CustomPolygonModel.id == custom_polygon_id).one_or_none()
        
            if not custom_polygon:
                return make_response(jsonify({"error": "Custom Polygon not found"}), 404)

            return jsonify(custom_polygon.to_dict)

    def put(self, database, custom_polygon_id):
        data = json.loads(request.data)
        
        with DB_SESSION[db_selector(database)].begin() as db_session:
            custom_polygon = db_session.query(CustomPolygonModel).filter(CustomPolygonModel.id == custom_polygon_id).one_or_none()

        if not custom_polygon:
            return make_response(jsonify({"error": "Custom Polygon not found"}), 404)

        custom_polygon.updated_at = datetime.now()

        if data.get('geometry'):
            try:
                geometry = from_geojson(json.dumps(data.get('geometry')))
                if geometry.geom_type != 'Polygon':
                    return make_response(jsonify({'error': 'Geometry must be a Polygon'}), 406)
            except GEOSException:
                return make_response(jsonify({'error': 'Invalid geometry'}), 406)

            custom_polygon._geometry_wkb = from_shape(geometry)

        for additional_attribute in ['custom_group_id', 'reference', 'orbit', 'metadata', 'values']:
            if data.get(additional_attribute):
                setattr(custom_polygon, additional_attribute, data[additional_attribute])

        if custom_polygon.validates is True:
            with DB_SESSION[db_selector(database)].begin() as db_session:
                custom_polygon.metrics = custom_polygon.calculate_metrics(AccessHelper(), database)

                db_session.add(custom_polygon)
                db_session.commit()
                return make_response({"message": f"Custom Polygon {custom_polygon.id} updated"} | custom_polygon.to_dict, 200)
        else:
            return make_response(jsonify({"error": custom_polygon.validates}), 400)
    
    def delete(self, database, custom_polygon_id):
        with DB_SESSION[db_selector(database)].begin() as db_session:
            custom_polygon = db_session.query(CustomPolygonModel).filter(CustomPolygonModel.id == custom_polygon_id).one_or_none()

        if not custom_polygon:
            return make_response(jsonify({"error": "Custom Polygon not found"}), 404)

        db_session.delete(custom_polygon)
        db_session.commit()
        return make_response({"message": f"Custom Polygon {custom_polygon.id} deleted"}, 200)
