import yaml
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Tuple
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import io
import base64

from db.crud import *
from db.models import LoadParameters
from db.repositories import get_exp_parameters_by_category, get_exp_parameters_by_exp_hash
from macros_generator import load_calculation, recycling_calculation
from network.routes.ch_experimentdb_experiment_data_router import get_ch_experimentdb_experiment_data_by_file_id
from network.schemas import ExperimentParametersBody, ExperimentParametersPlot

router = APIRouter()


@router.post('/create')
async def create_experiment_parameters(body: ExperimentParametersBody):
    try:
        await create(ExperimentParameters,
                     outer_blades_count=body.outer_blades_count,
                     outer_blades_length=body.outer_blades_length,
                     outer_blades_angle=body.outer_blades_angle,
                     middle_blades_count=body.middle_blades_count,
                     load_id=body.load_id,
                     recycling_id=body.recycling_id,
                     experiment_hash=body.experiment_hash,
                     experiment_category_id=body.experiment_category_id
                     )

        return {"message": "Новая запись <ExperimentParameters> успешно добавлена"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")

@router.post('/plot')
async def create_plot(body: ExperimentParametersPlot):
    try:
        exp_data = await get_ch_experimentdb_experiment_data_by_file_id(body.file_id)
        if not exp_data:
            raise HTTPException(status_code=404, detail="Данные не найдены для указанного file_id")

        filtered_data = [
            record for record in exp_data
            if body.range[0] <= getattr(record, body.additional_dimension, None) <= body.range[1]
        ]

        if not filtered_data:
            return {"message": "Нет данных для отображения в заданном диапазоне"}

        x = [record.x for record in filtered_data]
        y = [record.y for record in filtered_data]
        z = [record.z for record in filtered_data]
        color_values = [getattr(record, body.additional_dimension, None) for record in filtered_data]

        fig = plt.figure(figsize=(10, 8))
        ax = fig.add_subplot(111, projection='3d')
        scatter = ax.scatter(x, y, z, c=color_values, cmap=cm.viridis, marker='o')

        cbar = fig.colorbar(scatter, ax=ax)
        cbar.set_label(body.additional_dimension)

        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')

        plt.title(f"3D график с измерением: {body.additional_dimension}")

        buf = io.BytesIO()
        plt.savefig(buf, format='png')
        buf.seek(0)
        image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
        buf.close()
        plt.close(fig)

        return {"message": "График построен успешно", "plot": image_base64}

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")

@router.get('/all')
async def get_all_experiment_parameters():
    try:
        result = await get_all(ExperimentParameters)

        if result is not None:
            return result
        else:
            return {"message": "Нет записей в <ExperimentParameters>"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")


@router.get('/{id}')
async def get_by_id_experiment_parameters(id: int):
    try:
        result = await get_by_id(ExperimentParameters, id)

        if result is not None:
            return result
        else:
            return {"message": "Запись <ExperimentParameters> не найдена"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")


@router.get('/by_category/{id}')
async def get_experiment_parameters_by_exp_category(id: int):
    try:
        result = await get_exp_parameters_by_category(id)

        if result is not None:
            return result
        else:
            return {"message": f'<ExperimentParameters> с идентификатором категории - {id} - не найдены'}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")


@router.get('/by_exp_hash/{hash}')
async def get_experiment_parameters_by_exp_category(hash: str):
    try:
        result = await get_exp_parameters_by_exp_hash(hash)

        if result is not None:
            return result
        else:
            return {"message": f'<ExperimentParameters> с experiment_hash = {id} - не найдены'}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")


@router.delete('/{id}/delete')
async def delete_experiment_parameters(id: int):
    try:
        is_deleted = await delete(ExperimentParameters, id)

        if is_deleted:
            return {"message": "Запись <ExperimentParameters> успешно удалена"}
        else:
            return {"message": "Запись <ExperimentParameters> не найдена"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")

# @router.post('/process_and_save/{id}') было нужно для проверки
async def process_and_save_experiment_data(id: int, load: float, recycling_level: float) -> dict:
    try:
        experiment = await get_by_id(ExperimentParameters, id)
        if experiment is None:
            raise HTTPException(status_code=404, detail=f"ExperimentParameters с id {id} не найден.")

        yaml_file_path = "config.yaml"

        with open(yaml_file_path, "r", encoding="utf-8") as file:
            data = yaml.safe_load(file)

        diameters = data["parameters"]["diameters"]

        dict_load = load_calculation(load, diameters, None)

        primary_air_consumption = dict_load["primary_air_consumption"]
        secondary_air_consumption = dict_load["secondary_air_consumption"]
        gas_inlet_consumption = dict_load["gas_inlet_consumption"]
        alpha = dict_load["alpha"]
        gas_consumption = dict_load["gas_consumption"]
        air_consumption = dict_load["air_consumption"]

        dict_recycling = recycling_calculation(alpha, gas_consumption, air_consumption, recycling_level)

        co2 = dict_recycling["CO2"]
        n2 = dict_recycling["N2"]
        h2o = dict_recycling["H2O"]
        o2 = dict_recycling["O2"]

        load_params = await create(
            LoadParameters,
            load=int(load),
            primary_air_consumption=primary_air_consumption,
            secondary_air_consumption=secondary_air_consumption,
            gas_inlet_consumption=gas_inlet_consumption
        )

        recycling_params = await create(
            RecyclingParameters,
            load_id=load_params.id,
            recycling_level=int(recycling_level),
            co2=co2,
            n2=n2,
            h2o=h2o,
            o2=o2
        )

        await update_exp(
            ExperimentParameters,
            id=experiment.id,
            updated_data={
                "load_id": load_params.id,
                "recycling_id": recycling_params.id
            }
        )

        return {
            "message": "Данные успешно обработаны и сохранены.",
            "load_parameters": load_params,
            "recycling_parameters": recycling_params
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")