"""Class that provides the Api with a list of routes from a Template"""

import logging
from typing import Iterator, List, Optional

from samcli.lib.providers.api_collector import ApiCollector
from samcli.lib.providers.cfn_api_provider import CfnApiProvider
from samcli.lib.providers.cfn_base_api_provider import CfnBaseApiProvider
from samcli.lib.providers.provider import AbstractApiProvider, Api, Stack
from samcli.lib.providers.sam_api_provider import SamApiProvider

LOG = logging.getLogger(__name__)


class ApiProvider(AbstractApiProvider):
    def __init__(self, stacks: List[Stack], cwd: Optional[str] = None):
        """
        Initialize the class with template data. The template_dict is assumed
        to be valid, normalized and a dictionary. template_dict should be normalized by running any and all
        pre-processing before passing to this class.
        This class does not perform any syntactic validation of the template.

        After the class is initialized, changes to ``template_dict`` will not be reflected in here.
        You will need to explicitly update the class with new template, if necessary.

        Parameters
        ----------
        stacks : dict
            List of stacks apis are extracted from
        cwd : str
            Optional working directory with respect to which we will resolve relative path to Swagger file
        """
        self.stacks = stacks

        # Store a set of apis
        self.cwd = cwd
        self.api = self._extract_api()
        self.routes = self.api.routes
        LOG.debug("%d APIs found in the template", len(self.routes))

    def get_all(self) -> Iterator[Api]:
        """
        Yields all the Apis in the current Provider

        :yields api: an Api object with routes and properties
        """

        yield self.api

    def _extract_api(self) -> Api:
        """
        Extracts all the routes by running through the one providers. The provider that has the first type matched
        will be run across all the resources

        Parameters
        ----------
        Returns
        ---------
        An Api from the parsed template
        """

        collector = ApiCollector()
        provider = ApiProvider.find_api_provider(self.stacks)
        provider.extract_resources(self.stacks, collector, cwd=self.cwd)
        return collector.get_api()

    @staticmethod
    def find_api_provider(stacks: List[Stack]) -> CfnBaseApiProvider:
        """
        Finds the ApiProvider given the first api type of the resource

        Parameters
        -----------
        stacks: List[Stack]
            List of stacks apis are extracted from

        Return
        ----------
        Instance of the ApiProvider that will be run on the template with a default of SamApiProvider
        """
        for stack in stacks:
            for _, resource in stack.resources.items():
                if resource.get(CfnBaseApiProvider.RESOURCE_TYPE) in SamApiProvider.TYPES:
                    return SamApiProvider()

                if resource.get(CfnBaseApiProvider.RESOURCE_TYPE) in CfnApiProvider.TYPES:
                    return CfnApiProvider()

        return SamApiProvider()
