import html
import json
import logging
import re
from abc import abstractmethod
from datetime import datetime, time
from typing import Optional

import requests

from moscow_routes_parser.model import Route, Timetable, Equipment, Timetable_builder
from moscow_routes_parser.model_impl import Timetable_builder_t_mos_ru


class parser_timetable:
    """"Interface for parser"""

    @abstractmethod
    def parse(self, text: str) -> Timetable_builder:
        pass


class parser_timetable_t_mos_ru(parser_timetable):
    """"Parser for timetable from t.mos.ru implementation"""

    def __init__(self, builder: Timetable_builder):
        """"Initialize parser
        :param builder: Builder for Timetable for route
        """
        self.builder = lambda: builder

    def parse(self, text: str) -> Timetable_builder:
        """Parse text from https://transport.mos.ru/ru/ajax/App/ScheduleController/getRoute (for format using
        2022-Jan-11)

        Since 12.01.2022 t.mos.ru drop data-services from results
        Since 13.03.2022 added flag_has_another_direction
        @param text: text for parse
        @return Timetable for route
        """

        result_stops = type(self.builder())()
        # stops = re.finditer(r'data-stop="([^"]*?)".*?data-services="([^"]*?)".*?d-inline.*?>(.*?)<(.*?)</li>', text,
        #                     re.M + re.S
        #                     )
        stops = re.finditer(r'data-stop="(.*?)".*?d-inline.*?>(.*?)<(.*?)</li>', text,
                            re.M + re.S
                            )
        data_coords_iter = re.finditer(r'data-coords="(.*?)"', text,
                                       re.M + re.S
                                       )
        data_coords_list = list(data_coords_iter)
        if re.search(r'ic-change-a-b', text, re.M + re.S) is None:
            result_stops.set_has_another_direction(False)
        else:
            result_stops.set_has_another_direction(True)
        # если есть расписание
        if len(data_coords_list) > 0:
            data_coords = data_coords_list[0].group(1)
            data_coords = html.unescape(data_coords)
            data_coords = json.loads(data_coords)['features']
            data_coords = iter(map(lambda feature: feature['geometry']['coordinates'], data_coords))
        else:
            data_coords = []

        for stop in stops:
            name_stop = stop.group(2)
            coords_stop = next(data_coords)
            description = stop.group(3)
            logger = logging.getLogger(__name__)
            logger.info(name_stop)
            hours = re.finditer(r'dt1.*?(\d\d):(.*?)</div>\s*</div>\s*</div>', description, re.M + re.S)
            timetable_stop = result_stops.add_stop()
            timetable_stop.set_name(name_stop)
            timetable_stop.set_coords(coords_stop)
            log_timetable = ""
            for hour in hours:
                num_hour = int(hour.group(1))
                minutes_text = hour.group(2)
                log_timetable += str(num_hour) + ": "
                minutes = re.finditer(r'div10([^>]*)>\s*(\d\d)', minutes_text, re.M + re.S)
                for minute in minutes:
                    num_minute = int(minute.group(2))
                    color_start = minute.group(1).find('color: ')
                    if color_start >= 0:
                        quote = minute.group(1).find('"', color_start)
                        min_color = minute.group(1)[color_start + 7:quote]
                    else:
                        min_color = None
                    if not (min_color is None):
                        log_timetable += "{}{}".format(num_minute, min_color) + " "
                        pass
                    else:
                        log_timetable += str(num_minute) + " "
                        pass
                    time_flight = time(num_hour, num_minute)
                    timetable_stop.add_item_timetable(time_flight, min_color)
                logger.info(log_timetable)
        return result_stops


class Parser_routes:

    @abstractmethod
    def parse(self, text: str) -> [Route]:
        pass


class Parser_routes_t_mos_ru(Parser_routes):

    def __init__(self):
        self.count = None

    def parse(self, text: str) -> [Route]:
        """"Parses route info from transport.mos.ru (name, id, type)
        :param text: text for parsing from t.mos.ru
        :return list of Route
        """
        count_result = re.finditer(r'data-count-pages="(\d+)"', text, re.M + re.S)
        self.count = int(list(count_result)[0].group(1))

        result = re.finditer(r'<a.*?href=.*?route/(.+?)".*?<div.*?ic[ ]([a-z-]+).*?</i>\s*(\S+?)\s*</div>', text,
                             re.M + re.S)
        list_routes = []

        for route in result:
            num = route.group(1)
            type_route = route.group(2)
            if type_route.find('-bus') >= 0:
                type_route = Equipment.bus()
            elif type_route.find('tramway') >= 0:
                type_route = Equipment.tramway()
            elif type_route.find('trolleybus') >= 0:
                type_route = Equipment.trolleybus()
            else:
                logging.getLogger(__name__).error("Unknown type route: {}".format(type_route))
                type_route = None
            name = route.group(3)
            list_routes.append(Route(num, type_route, name))
        return list_routes


def get_route(date: datetime.date, id_route_t_mos_ru: str, direction: int,
              get_route_url: str = 'https://transport.mos.ru/ru/ajax/App/ScheduleController/getRoute',
              parser: parser_timetable = parser_timetable_t_mos_ru(builder=Timetable_builder_t_mos_ru())
              ) -> Timetable:
    """Get timetable for route by date and direction
        :param date: date of timetable for route
        :param id_route_t_mos_ru: id of route from t.mos.ru
        :param direction: direction for route (0 or 1)
        :param get_route_url URL for requesting timetable
        :param parser for timetable
        :return timetable for route by date and direction
    """
    logger = logging.getLogger(__name__)
    try:
        # strange problem with SSL Cert in package
        response = requests.get(get_route_url,
                                params={
                                    'mgt_schedule[isNight]': '',
                                    'mgt_schedule[date]': date.strftime("%d.%m.%Y"),
                                    'mgt_schedule[route]': id_route_t_mos_ru,
                                    'mgt_schedule[direction]': direction,
                                },
                                headers={'X-Requested-With': 'XMLHttpRequest'}
                                )
        if response.status_code == 200:
            logger.info("Get route #{}".format(id_route_t_mos_ru))
            route_info = parser.parse(response.text)
        else:
            logger.error("Error status: {}".format(response.status_code))
            route_info = None
    except requests.exceptions.RequestException as e:
        logger.error("Error " + str(e))
        route_info = None

    if not (route_info is None):
        result = route_info.set_id_route_t_mos_ru(id_route_t_mos_ru).set_direction(direction).set_date(date).build()
        if len(result.get_stops()) == 0:  # Error of loading timetable without exceptions
            result = None
    else:
        result = None
    return result


def get_list_routes(work_time: int, direction: int,
                    parser: Parser_routes = None,
                    get_routes_url: str = 'https://transport.mos.ru/ru/ajax/App/ScheduleController/getRoutesList'
                    ) -> Optional[list[Route]]:
    """get list routes by work_time and direction from transport.mos.ru
        :param parser: function to parse got string
        :param get_routes_url: url for requesting routes
        :param work_time: work day or not (1 or 0)
        :param direction: 0
        :return list of Route
    """
    if parser is None:
        parser = Parser_routes_t_mos_ru()
    page = 1
    result_routes = []
    finish = False
    count = None
    logger = logging.getLogger(__name__)
    while not finish:
        finish = False
        repeat = True
        while repeat:
            repeat = False
            try:
                # strange problem with SSL Cert in package
                response = requests.get(get_routes_url,
                                        params={
                                            'mgt_schedule[search]': '',
                                            'mgt_schedule[isNight]': '',
                                            #  'mgt_schedule[filters]': '',
                                            'mgt_schedule[work_time]': work_time,
                                            'page': page,
                                            'mgt_schedule[direction]': direction,
                                        }
                                        , headers={'X-Requested-With': 'XMLHttpRequest'}
                                        # , headers={'Cookie': "_ym_d=1637468102; _ym_uid=1637468102592825648; mos_id=rBEAAmGaFNawBwAOHRgWAgA=; _ga=GA1.2.1733238845.1637487830; uxs_uid=147e2110-500d-11ec-a7cb-8bb8b12c3186; KFP_DID=ee285837-cd1f-0a9b-c8a2-9cef6a4ee333; _ym_isad=2; _ym_visorc=w"}
                                        )
                if response.status_code == 200:
                    logger.info("Get page #{}".format(page))
                    routes = parser.parse(response.text)
                    result_routes += routes
                    if count is None:
                        count = parser.count
                    if not routes:
                        finish = True
                else:
                    logger.error("Error status: {}".format(response.status_code))
                    finish = True
                page = page + 1
                if page > count:
                    finish = True
            except requests.exceptions.RequestException as e:
                logger.error("Error " + str(e))
                repeat = True

    return result_routes

[((6138, 6165), 'logging.getLogger', 'logging.getLogger', (['__name__'], {}), '(__name__)\n', (6155, 6165), False, 'import logging\n'), ((8193, 8220), 'logging.getLogger', 'logging.getLogger', (['__name__'], {}), '(__name__)\n', (8210, 8220), False, 'import logging\n'), ((1418, 1504), 're.finditer', 're.finditer', (['"""data-stop="(.*?)".*?d-inline.*?>(.*?)<(.*?)</li>"""', 'text', '(re.M + re.S)'], {}), '(\'data-stop="(.*?)".*?d-inline.*?>(.*?)<(.*?)</li>\', text, re.M +\n    re.S)\n', (1429, 1504), False, 'import re\n'), ((1586, 1639), 're.finditer', 're.finditer', (['"""data-coords="(.*?)\\""""', 'text', '(re.M + re.S)'], {}), '(\'data-coords="(.*?)"\', text, re.M + re.S)\n', (1597, 1639), False, 'import re\n'), ((4468, 4527), 're.finditer', 're.finditer', (['"""data-count-pages="(\\\\d+)\\""""', 'text', '(re.M + re.S)'], {}), '(\'data-count-pages="(\\\\d+)"\', text, re.M + re.S)\n', (4479, 4527), False, 'import re\n'), ((4603, 4724), 're.finditer', 're.finditer', (['"""<a.*?href=.*?route/(.+?)".*?<div.*?ic[ ]([a-z-]+).*?</i>\\\\s*(\\\\S+?)\\\\s*</div>"""', 'text', '(re.M + re.S)'], {}), '(\n    \'<a.*?href=.*?route/(.+?)".*?<div.*?ic[ ]([a-z-]+).*?</i>\\\\s*(\\\\S+?)\\\\s*</div>\'\n    , text, re.M + re.S)\n', (4614, 4724), False, 'import re\n'), ((1781, 1826), 're.search', 're.search', (['"""ic-change-a-b"""', 'text', '(re.M + re.S)'], {}), "('ic-change-a-b', text, re.M + re.S)\n", (1790, 1826), False, 'import re\n'), ((2116, 2142), 'html.unescape', 'html.unescape', (['data_coords'], {}), '(data_coords)\n', (2129, 2142), False, 'import html\n'), ((2520, 2547), 'logging.getLogger', 'logging.getLogger', (['__name__'], {}), '(__name__)\n', (2537, 2547), False, 'import logging\n'), ((2603, 2695), 're.finditer', 're.finditer', (['"""dt1.*?(\\\\d\\\\d):(.*?)</div>\\\\s*</div>\\\\s*</div>"""', 'description', '(re.M + re.S)'], {}), "('dt1.*?(\\\\d\\\\d):(.*?)</div>\\\\s*</div>\\\\s*</div>', description, \n    re.M + re.S)\n", (2614, 2695), False, 'import re\n'), ((5688, 5716), 'moscow_routes_parser.model_impl.Timetable_builder_t_mos_ru', 'Timetable_builder_t_mos_ru', ([], {}), '()\n', (5714, 5716), False, 'from moscow_routes_parser.model_impl import Timetable_builder_t_mos_ru\n'), ((2169, 2192), 'json.loads', 'json.loads', (['data_coords'], {}), '(data_coords)\n', (2179, 2192), False, 'import json\n'), ((3072, 3139), 're.finditer', 're.finditer', (['"""div10([^>]*)>\\\\s*(\\\\d\\\\d)"""', 'minutes_text', '(re.M + re.S)'], {}), "('div10([^>]*)>\\\\s*(\\\\d\\\\d)', minutes_text, re.M + re.S)\n", (3083, 3139), False, 'import re\n'), ((4944, 4959), 'moscow_routes_parser.model.Equipment.bus', 'Equipment.bus', ([], {}), '()\n', (4957, 4959), False, 'from moscow_routes_parser.model import Route, Timetable, Equipment, Timetable_builder\n'), ((5376, 5404), 'moscow_routes_parser.model.Route', 'Route', (['num', 'type_route', 'name'], {}), '(num, type_route, name)\n', (5381, 5404), False, 'from moscow_routes_parser.model import Route, Timetable, Equipment, Timetable_builder\n'), ((8440, 8685), 'requests.get', 'requests.get', (['get_routes_url'], {'params': "{'mgt_schedule[search]': '', 'mgt_schedule[isNight]': '',\n    'mgt_schedule[work_time]': work_time, 'page': page,\n    'mgt_schedule[direction]': direction}", 'headers': "{'X-Requested-With': 'XMLHttpRequest'}"}), "(get_routes_url, params={'mgt_schedule[search]': '',\n    'mgt_schedule[isNight]': '', 'mgt_schedule[work_time]': work_time,\n    'page': page, 'mgt_schedule[direction]': direction}, headers={\n    'X-Requested-With': 'XMLHttpRequest'})\n", (8452, 8685), False, 'import requests\n'), ((3864, 3890), 'datetime.time', 'time', (['num_hour', 'num_minute'], {}), '(num_hour, num_minute)\n', (3868, 3890), False, 'from datetime import datetime, time\n'), ((5039, 5058), 'moscow_routes_parser.model.Equipment.tramway', 'Equipment.tramway', ([], {}), '()\n', (5056, 5058), False, 'from moscow_routes_parser.model import Route, Timetable, Equipment, Timetable_builder\n'), ((5141, 5163), 'moscow_routes_parser.model.Equipment.trolleybus', 'Equipment.trolleybus', ([], {}), '()\n', (5161, 5163), False, 'from moscow_routes_parser.model import Route, Timetable, Equipment, Timetable_builder\n'), ((5198, 5225), 'logging.getLogger', 'logging.getLogger', (['__name__'], {}), '(__name__)\n', (5215, 5225), False, 'import logging\n')]