from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware  # CORS
from fastapi.middleware.gzip import GZipMiddleware  # gZip

from enum import Enum

import sqlite3
from datetime import datetime

class ParameterName(str, Enum):
    co = "co"
    no2 = "no2"
    no = "no"
    pm10 = "pm10"
    pm25 = "pm25"

app = FastAPI()

app.add_middleware(GZipMiddleware, minimum_size=1000)  # gZip

origins = [ # CORS
    "*",
]

app.add_middleware(  # CORS
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

DB_LOCATION = "./db.sqlite"

con = sqlite3.connect(DB_LOCATION)
con.row_factory
cur = con.cursor()


@app.get("/")
async def root():
    return "Hello World!"

@app.get("/parameters")
async def get_parameters():
    """Возращает доступные показатели
    """

    cur.execute(
        """SELECT   
                parameter_id,
                parameter_name
            FROM parameters
        """
    )

    parameters = cur.fetchall()

    return [
        {
            "parameter_id": row[0],
            "parameter_name": row[1]
        }
        for row in parameters
    ]


@app.get("/stations")
async def get_stations():
    """Возращает названия и координаты станций 
    """

    cur.execute(
        """SELECT   
                station_id,
                st_name,
                lat,
                lon
            FROM stations
        """
    )

    stations = cur.fetchall()

    return [
        {
            "station_id": row[0],
            "st_name": row[1],
            "lat": row[2],
            "lon": row[3]
        }
        for row in stations
    ]


@app.get("/stations/{parameter}/{start_datetime}/{end_datetime}")
async def get_stations_parameter_values(parameter: ParameterName, start_datetime: datetime, end_datetime: datetime):
    """Возращает значения в станциях на заданный временной интервал для выбранного показателя. Для каждого показателя приводиться список значений на каждый час
    в заданном интервале, включая `start_datetime` и не включая `end_datetime`.
    
    Использовать дату и время вида 2020-12-31T17:00
    """

    cur.execute(
        """SELECT   
                station_id,
                datetime,
                value,
                forecast
            FROM stations_all_params
            JOIN parameters ON stations_all_params.parameter = parameters.parameter_id
            WHERE parameter_name = :parameter AND datetime >= strftime('%s', :start_datetime) AND datetime < strftime('%s', :end_datetime)
        """,
        {
            "parameter": parameter,
            "start_datetime": start_datetime,
            "end_datetime": end_datetime
        }
    )

    stations_values = cur.fetchall()

    measured = [row for row in stations_values if not row[3]]
    try:
        measured_max_datetime = max([row[1] for row in measured])
    except:
        return []
    forecast = [row for row in stations_values if row[1] > measured_max_datetime]
    clear_stations_values = measured + forecast

    return [
        {
            "station_id": row[0],
            "datetime": row[1],
            "value": row[2],
            "forecast": row[3]
        }
        for row in clear_stations_values
    ]


@app.get("/station/{station_id}/{start_datetime}/{end_datetime}")
async def get_one_station_values(station_id: int, start_datetime: datetime, end_datetime: datetime):
    """Возращает значения в станции на заданный временной интервал для всех показателей. Для каждого показателя приводиться список значений на каждый час
    в заданном интервале, включая `start_datetime` и не включая `end_datetime`.
    
    Использовать дату и время вида 2020-12-31T17:00
    """

    cur.execute(
        """SELECT
                datetime,
                parameter,
                value
            FROM stations_all_params
            WHERE station_id = :station_id AND datetime >= strftime('%s', :start_datetime) AND datetime < strftime('%s', :end_datetime)
        """,
        {
            "station_id": station_id,
            "start_datetime": start_datetime,
            "end_datetime": end_datetime
        }
    )

    one_station_values = cur.fetchall()

    return {
            "station_id": station_id,
            "datetime": set([row[0] for row in one_station_values]),
            "co": [row[2] for row in one_station_values if row[1] == 1],
            "no2": [row[2] for row in one_station_values if row[1] == 2],
            "no": [row[2] for row in one_station_values if row[1] == 3],
            "pm10": [row[2] for row in one_station_values if row[1] == 4],
            "pm25": [row[2] for row in one_station_values if row[1] == 5]
    }

@app.get("/stations/dates")
async def get_stations_dates():
    """Возращает минимальную и максимальную даты, которые есть в таблице сетки.
    """

    cur.execute(
        f"""SELECT   
                MIN(datetime), MAX(datetime)
            FROM stations_all_params
     """
    )

    stations_dates = cur.fetchall()
    return(stations_dates[0])


@app.get("/basenet")
async def get_basenet_geo():
    """Возращает центры ячеек и ближайшую станцию, соответствующую каждой ячейке 
    """

    cur.execute(
        """SELECT   
                grid_id,
                lat,
                lon,
                station_id
            FROM basenet
        """
    )

    basenet = cur.fetchall()

    return [
        {
            "grid_id": row[0],
            "lat": row[1],
            "lon": row[2],
            "station_id": row[3]
        }
        for row in basenet
    ]   


@app.get("/net/{parameter}/{start_datetime}/{end_datetime}")
async def get_net_parameter_values(parameter: ParameterName, start_datetime: datetime, end_datetime: datetime):
    """Возращает значения ячеек на заданный временной интервал для выбранного показателя. Для каждого показателя приводиться список значений на каждый час
    в заданном интервале, включая `start_datetime` и не включая `end_datetime`.
    
    Использовать дату и время вида 2020-12-31T17:00 (пока есть данные только для 2020-12-31 и 2021-01-01)
    """

    cur.execute(
        """SELECT   
                grid_id,
                datetime,
                param_value,
                forecast
            FROM net_all_params
            JOIN parameters ON net_all_params.parameter = parameters.parameter_id
            WHERE parameter_name = :parameter AND datetime >= strftime('%s', :start_datetime) AND datetime < strftime('%s', :end_datetime)
        """,
        {
            "parameter": parameter,
            "start_datetime": start_datetime,
            "end_datetime": end_datetime
        }
    )

    net_values = cur.fetchall()

    measured = [row for row in net_values if not row[3]]
    try:
        measured_max_datetime = max([row[1] for row in measured])
    except:
        []
    forecast = [row for row in net_values if row[1] > measured_max_datetime]
    clear_net_values = measured + forecast

    return [
        {
            "grid_id": row[0],
            "datetime": row[1],
            "value": row[2],
            "forecast": row[3]
        }
        for row in clear_net_values
    ]

@app.get("/net/dates")
async def get_net_dates():
    """Возращает минимальную и максимальную даты, которые есть в таблице сетки.
    """

    cur.execute(
        f"""SELECT   
                MIN(datetime), MAX(datetime)
            FROM net_all_params
     """
    )

    net_dates = cur.fetchall()
    return(net_dates[0])