#
# Imandra Inc.
#
# endpts_metamodel.py
#

import logging

from fastapi import HTTPException

from codelogician.modeling.mlist import ModelList, gen_listing

from ..cl_server import CLServer

log = logging.getLogger(__name__)


def register_metamodel_endpoints(app: CLServer):  # noqa: C901
    @app.get('/metamodel', operation_id='metamodel')
    async def latest_state():
        """
        Return the latest metaModel
        """
        log.info('Received request for the latest metamodel')

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        # if state.curr_meta_model is None: return {}
        return state.curr_meta_model.toJSON()

    # Return summary of the current state
    @app.get('/metamodel/summary', operation_id='metamodel_summary')
    async def metamodel_latest_summary():
        """
        Return summary of the latest metamodel
        """

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        log.info('Received /state/summary request')
        # already returns JSON representation
        # if state.curr_meta_model is None:
        #    return {}
        # else:
        return state.curr_meta_model.summary()

    @app.get('/metamodel/list', operation_id='list_of_models')
    async def model_list(listby: str = 'alphabet'):
        """
        Return a list of model statistics, sorted by specified criteria. Options are:
        - None - then straight up list of models
        - frm_status - formalization status
        - upstream - number of models upstream which are affected by the specified model
        - opaques - number of opaque functions
        - failed_vgs - number of failed verification goals
        """

        log.info(f'Received request for model listing by {listby}.')

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=400, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=400, detail=f'Failed to get strategy state: {e}'
            )

        try:
            if listby == 'alphabet':
                return ModelList(models=state.curr_meta_model.list_models())
            else:
                return gen_listing(models=state.curr_meta_model.models, category=listby)
        except Exception as e:
            raise HTTPException(
                status_code=400, detail=f'Error when requesting listing: {str(e)}'
            )

    @app.get('/metamodel/vgs', operation_id='list_of_vgs')
    async def verification_goals():
        """
        Return verification goals with their statuses for the entire metamodel
        """

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        log.info('Received /state/latest/vgs request')
        if state.curr_meta_model is None:
            return []

        return state.curr_meta_model.vgs()

    @app.get('/metamodel/decomps', operation_id='list_of_decomps')
    async def decomps():
        """
        Return the list of decomps
        """

        log.info('Received /metamodel/decomps request')

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        if state.curr_meta_model is None:
            return []
        return state.curr_meta_model.decomps()

    @app.get('/metamodel/opaques', operation_id='list_of_opaque_functions')
    async def opaques():
        """
        Return list of opaque functions
        """

        log.info('Received /state/latest/opaques request')

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        if state.curr_meta_model is None:
            return []
        return state.curr_meta_model.opaques()

    @app.get('/metamodel/cache/by_index/{idx}', operation_id='get_metamodel_from_cache')
    async def get_state(idx: int):
        """
        Returns a complete state of the cache.
        """
        log.info('Received cache metamodel request for index {idx}')

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        if idx in state.meta_cache.indices():
            mmodel = state.meta_cache.get_cache_metamodel(idx)

            if mmodel is None:
                raise HTTPException(
                    status_code=404,
                    detail=f'Metamodel from cache with index={idx} not found!',
                )
            else:
                return mmodel.toJSON()
        else:
            log.error(f'{idx} index not found!')
            raise HTTPException(
                status_code=404,
                detail=f'Metamodel from cache with index={idx} not found!',
            )

    @app.get('/metamodel/cache/indices', operation_id='get_metamodel_cache_indices')
    async def cache_indices() -> list[int]:
        """
        Return the list of all indices in cache.
        """

        log.info('Request for all indices.')

        strategy_id = app.current_strat_id
        if strategy_id is None:
            raise HTTPException(status_code=404, detail='No current strategy')

        try:
            state = app.strategy_state_by_id(strategy_id)
        except Exception as e:
            raise HTTPException(
                status_code=404, detail=f'Failed to get strategy state: {e}'
            )

        return state.meta_cache.indices()
