import sys
import os
import logging
from flask import Flask, jsonify, send_from_directory, request
from dotenv import load_dotenv
from sqlalchemy.orm import sessionmaker, Session
from modules.driverHandler import DriverHandler
from modules.gpxInterpreter import GPXHandler
from modules.vehicleHandler import VehicleHandler
from modules.geoObjects import Waypoint, create_table, db_connect, Driver, Vehicle
from flask_cors import CORS, cross_origin

root = logging.getLogger()
root.setLevel(logging.DEBUG)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)

print("load environment")
load_dotenv()

print("create flask application")
app = Flask(__name__, static_folder='web/dist')
cors = CORS(app)  # allow CORS for all domains on all routes.
app.config['CORS_HEADERS'] = 'Content-Type'
app.logger.setLevel(logging.DEBUG)

print("initialize database")
engine = db_connect()
SessionFactory = sessionmaker(bind=engine)
session = SessionFactory()
create_table(engine)

print("pushing initial data if not already happened")
try:
    defDriver = session.query(Driver).filter(id=1)
    defVehicle = session.query(Vehicle).filter(id=1)
    if not defDriver and not defVehicle:
        defaultVehicle = Vehicle(id=1, name="default", licenseplate="default")
        defaultDriver = Driver(id=1, name="default")

        session.add(defaultVehicle)
        session.add(defaultDriver)
        session.commit()
        session.flush()
except:
    print("default data not pushed as already present")

print("initialize handlers")
gpxHandler = GPXHandler(session)
driverHandler = DriverHandler(session)
vehicleHandler = VehicleHandler(session)


@app.route('/')
@cross_origin()
def serve_vue_app():
    return send_from_directory(app.static_folder, 'index.html')


@app.route("/track", methods=['GET', 'DELETE'])
@cross_origin()
def getTrack():

    app.logger.debug(f"found arguments {request.args}")

    match request.method:
        case "GET":
            if ("start" in request.args and "end" in request.args) or ("id" in request.args):
                if "start" in request.args and "end" in request.args:
                    if "asMap" in request.args:
                        # get tracks by filter
                        start = request.args["start"]
                        end = request.args["end"]

                        # Die GeoJSON-Daten aus der Datenbank abrufen
                        geojson_data = gpxHandler.getTracksInTimeWithGeoData(
                            start, end)
                        app.logger.debug(f"returned track {geojson_data}")

                        # Die GeoJSON-Daten als JSON zurückgeben
                        return jsonify(geojson_data)

                    # get tracks by filter
                    start = request.args["start"]
                    end = request.args["end"]
                    try:
                        return gpxHandler.getTracksInTime(start, request.args["end"]), 200
                    except Exception as e:
                        app.logger.debug(f"failed to search tracks error {
                                        e} values: start={start}, end={end}")
                        return f"error {e}", 500

                elif "id" in request.args:
                    # get track by id
                    trackID = int(request.args["id"])
                    try:
                        app.logger.debug(f"Request args: {request.args}")
                        app.logger.debug(f"track id {trackID}")
                        track = gpxHandler.getTrack(trackID)

                        app.logger.debug(f"returned track {track}")

                        return jsonify(track), 200
                    except Exception as e:
                        app.logger.debug(f"fetching track failed with error {e}")
                        return f"error {e}", 500
            else:
                try:
                    tracks = gpxHandler.getTracks()
                    if len(tracks) > 0:
                        # gets all tracks as list
                        return tracks, 200
                    else:
                        return [], 200
                except Exception as e:
                    app.logger.debug(f"fetching all tracks failed with error {e}")
                    return f"error {e}", 500
        case "DELETE":
            data = request.get_json()
            
            if "id" not in data:
                return "no if found", 400
            
            try:
                gpxHandler.deleteTrack(data["id"])
                return "track deleted", 200
            except Exception as e:
                return f"couldnt delete track due to error: {e}", 500


@app.route("/track/meta", methods=['GET'])
@cross_origin()
def getTrackMeta():

    app.logger.debug(f"found arguments {request.args}")

    if "id" in request.args:
        # get track by id
        trackID = int(request.args["id"])
        try:
            app.logger.debug(f"Request args: {request.args}")
            app.logger.debug(f"track id {trackID}")
            track = gpxHandler.getTrackMeta(trackID)

            app.logger.debug(f"returned track {track}")

            return jsonify(track), 200
        except Exception as e:
            app.logger.debug(f"fetching track failed with error {e}")
            return f"error {e}", 500


@app.route("/driver", methods=['GET', 'POST', 'PUT', 'DELETE'])
@cross_origin()
def handleDriverRoute():

    match request.method:
        case "GET":
            if 'driver' in request.args:

                try:
                    driver = driverHandler.getDriver(int(request.args["driver"]))
                    return driver, 200
                except Exception as e:
                    app.logger.debug(f"getting driver failed with error {e}")
                    return "error" + " " + str(e), 500

            else:

                try:
                    drivers = driverHandler.getDrivers()
                    if len(drivers) > 0:
                        return drivers, 200
                except Exception as e:
                    app.logger.debug(f"getting drivers failed with error {e}")
                    return "error" + " " + str(e), 500
        case "POST":
            # grabbing json from request
            data = request.get_json()
            app.logger.debug(f"json request payload: {data}")

            if "name" not in data:
                app.logger.debug(f"no driver name was found in request")
                return "missing name", 400

            app.logger.debug(f"driver name has passed check {data["name"]}")

            # handle creating vehicle
            try:
                app.logger.debug(f"json request name: {data["name"]}")
                driver = driverHandler.createDriver(data["name"])
                return jsonify({"name": driver.name, "id": driver.id}), 200

            except Exception as e:
                app.logger.debug(f"creating drivers failed with error {e}")
                return "error" + " " + str(e), 500
        case "DELETE":
            data = request.get_json()
            
            if "id" not in data:
                return "no if found", 400
            
            try:
                if data["id"] == 1:
                    return "deleting defaults is forbidden", 400
                
                driverHandler.deleteDriver(data["id"])
                return "driver deleted", 200
            except Exception as e:
                return f"couldnt delete driver due to error: {e}", 500


@app.route("/vehicle", methods=['GET', 'POST', 'PUT', 'DELETE'])
@cross_origin()
def handleVehicleRoute():

    match request.method:
        case "GET":
            if 'vehicle' in request.args:
                app.logger.debug(f"no vehicle id was found in request")
                vehicle = int(request.args["vehicle"])

                try:
                    foundVehicle = vehicleHandler.getVehicle(vehicle)
                    return foundVehicle, 200
                except Exception as e:
                    app.logger.debug(f"getting vehicle {vehicle} failed with error {e}")
                    return "error" + " " + str(e), 500

            else:

                try:
                    foundVehicles = vehicleHandler.getVehicles()
                    return foundVehicles, 200
                except Exception as e:
                    app.logger.debug(
                        f"getting all vehicles failed with error {e}")
                    return "error" + " " + str(e), 500
        case "POST":
            data = request.get_json()
            app.logger.debug(f"json request payload: {data}")

            if "name" not in data:
                return "missing name", 400

            licenseplate = ""

            if "licensePlate" not in data:
                licenseplate = "N/A"
            else:
                licenseplate = data["licensePlate"]
            name = data["name"]

            # handle creating vehicle
            try:
                vehicle = vehicleHandler.createVehicle(name, licenseplate)
                return jsonify({"id": vehicle.id, "name": vehicle.name}), 200

            except Exception as e:
                app.logger.debug(f"creating vehicle with name {name} failed with error {e}")
                return "error" + " " + str(e), 500
        case "DELETE":
            data = request.get_json()
            
            if "id" not in data:
                return "no if found", 400
            
            try:
                if data["id"] == 1:
                    return "deleting defaults is forbidden", 400
                
                vehicleHandler.deleteVehicle(data["id"])
                return "vehicle deleted", 200
            except Exception as e:
                return f"couldn't delete vehicle due to error: {e}", 500


@app.route('/upload', methods=['POST'])
@cross_origin()
def uploadFile():
    if 'file' not in request.files:
        app.logger.debug("no file was found in clients request")
        return "no file provided", 400

    file = request.files['file']
    if file.filename == '':
        app.logger.debug("filename was found empty")
        return "no file selected", 400

    try:
        app.logger.debug(f"Received file: {file.filename}, size: {len(file.read())} bytes")

        file.seek(0)

        file_path = f'./uploads/{file.filename}'
        with open(file_path, 'wb') as f:
            f.write(file.read())

        driverID = int(request.form.get('driverID'))
        vehicleID = int(request.form.get('vehicleID'))

        driver = driverHandler.getDriver(driverID)
        vehicle = vehicleHandler.getVehicle(vehicleID)

        app.logger.debug(f"driver {driver.id}")
        app.logger.debug(f"vehicle {vehicle.id}")

        if not driver or not vehicle:
            raise ValueError("Driver or vehicle not found")

        app.logger.debug(f"attempting to parse file: {file.filename}")
        gpxHandler.parse(file.filename, driver, vehicle)

        return "file stored succesfull", 200
    except Exception as e:
        app.logger.debug(f"storing gpx file failed with error {e}")
        return "error" + " " + str(e), 500


if __name__ == '__main__':
    app.run(debug=True)
    app.logger.debug("flask app started")
