from __future__ import annotations
import re
from pathlib import Path

from .androidcore import _AndroidCore
from ...logger import _logger
from ...out import GreppedOut


class React(_AndroidCore):
    """
    Module to handle analysis of apps developed using React. React is 
    a JavaScript framework, and the React files are usually found in 
    unzipped_folder/assets/index.android.bundle. This class inherits 
    all properties from _AndroidCore.

    >>> from glorifiedgrep.android.modules.reactapp import React
    >>> r = React('/path/to/apk)
    """

    @_logger
    def _react_path(self) -> str:
        """
        Gets the path of the React app bundle

        :return: path of React app
        :rtype: str
        """

        path = f'{self._unzipped_path}/assets/index.android.bundle'
        if not Path(path).exists():
            self.log_exception('Not a react app')
            raise TypeError('This is not a react app')
        self.log_debug(f'react path: {path}')
        return path

    @_logger
    def react_find_functions(self) -> list:
        """
        Find a list of all global functions and their parameters
        
        Returns
        -------
        list
            Array of function names and their parameters

        Examples
        --------
        >>> r.react_find_functions()
        """

        regex = r'^function\s(\w+)\((.*)\)'
        match = self._run_rg(
            regex=regex, path=self._react_path(), group="'-r $1 $2'")
        functions = list()
        for m in match:
            f = dict()
            m = m.decode('utf-8').split()
            f['line'] = m[0].strip(':')
            f['name'] = m[1]
            if len(m) > 2:
                f['parameters'] = ''.join(m[2:]).split(',')
            else:
                f['parameters'] = None
            functions.append(self._pdict(f))
        self.log_debug('')
        return functions

    @_logger
    def react_find_urls(self, show_code: bool=False) -> GreppedOut:
        """
        Find all URL's inside the React code bundle

        Parameters
        ----------
        show_code : bool, optional
            Show the full matched line, by default False
        
        Returns
        -------
        GreppedOut
            GreppedOut object

        Examples
        --------
        >>> r.react_find_urls()
        """

        regex = r'(?:http(s)?://)[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[.]@&\(\)\*\+]+|https?://localhost:?(\d+)?'
        match = self._run_rg(
            regex=regex, path=self._react_path(), code=show_code)
        found = list()
        for m in match:
            m = m.decode('utf-8').split(':')
            found.append(
                self._pdict({
                    'line': m[0],
                    'url': ':'.join(m[1:])
                })
            )
        self.log_debug('')
        return GreppedOut(found)

    @_logger
    def react_regex_constructor(self, show_code: bool=False) -> GreppedOut:
        """
        Find all RexExp constructors in the code

        Parameters
        ----------
        show_code : bool, optional
            Show the full matched line, by default False
        
        Returns
        -------
        GreppedOut
            GreppedOut object

        Examples
        --------
        >>> r.react_regex_constructor()
        """

        regex = r'new RegExp\('
        match = self._run_rg(
            regex=regex, path=self._react_path(), code=show_code)
        found = list()
        for m in match:
            m = m.decode('utf-8').split(':')
            found.append(
                self._pdict({
                    'line': m[0],
                    'match': ':'.join(m[1:])
                })
            )
        self.log_debug('')
        return GreppedOut(found)

    @_logger
    def react_xmlhttprequest_constructor(self, show_code: bool=False) -> GreppedOut:
        """
        Find all XMLHttpRequest constructors in the code

        Parameters
        ----------
        show_code : bool, optional
            Show the full matched line, by default False
        
        Returns
        -------
        GreppedOut
            GreppedOut object

        Examples
        --------
        >>> r.react_xmlhttprequest_constructor()
        """

        regex = r'new XMLHttpRequest\('
        match = self._run_rg(
            regex=regex, path=self._react_path(), code=show_code)
        found = list()
        for m in match:
            m = m.decode('utf-8').split(':')
            found.append(
                self._pdict({
                    'line': m[0],
                    'match': ':'.join(m[1:])
                })
            )
        self.log_debug('')
        return GreppedOut(found)

    @_logger
    def react_dev_global(self, show_code: bool=False) -> GreppedOut:
        """
        Find the __DEV__ variable and see what it is set to. 
        | `Reference <https://facebook.github.io/react-native/docs/javascript-environment.html>`__

        Parameters
        ----------
        show_code : bool, optional
            Show the full matched line, by default False
        
        Returns
        -------
        GreppedOut
            GreppedOut object

        Examples
        --------
        >>> r.react_dev_global()
        """

        regex = r'__DEV__.+(true|false)'
        match = self._run_rg(
            regex=regex, path=self._react_path(), code=show_code)
        found = list()
        for m in match:
            m = m.decode('utf-8').split(':')
            found.append(
                self._pdict({
                    'line': m[0],
                    'match': ':'.join(m[1:])
                })
            )
        self.log_debug('')
        return found

    @_logger
    def react_console_statements(self, show_code: bool=False) -> GreppedOut:
        """
        Find all the console statements. This includes log, warn, error, trace etc.  
        | `Reference <https://facebook.github.io/react-native/docs/javascript-environment.html>`__

        Parameters
        ----------
        show_code : bool, optional
            Show the full matched line, by default False
        
        Returns
        -------
        GreppedOut
            GreppedOut object

        Examples
        --------
        >>> r.react_console_statements()
        """

        regex = r'console\.(log|warn|error|info|trace|table|group|groupEnd)'
        match = self._run_rg(
            regex=regex, path=self._react_path(), code=show_code)
        found = self._pdict({
            'log': [],
            'warn': [],
            'error': [],
            'info': [],
            'trace': [],
            'table': [],
            'group': [],
            'groupEnd': []
        })
        for m in match:
            m = m.decode('utf-8').split(':')
            code = ':'.join(m[1:])
            key = re.findall(
                r'console\.(log|warn|error|info|trace|table|group|groupEnd)', code)[0]
            if key == 'log':
                found['log'].append(self._pdict({'line': m[0], 'match': code}))
            if key == 'warn':
                found['warn'].append(self._pdict(
                    {'line': m[0], 'match': code}))
            if key == 'error':
                found['error'].append(self._pdict(
                    {'line': m[0], 'match': code}))
            if key == 'info':
                found['info'].append(self._pdict(
                    {'line': m[0], 'match': code}))
            if key == 'trace':
                found['trace'].append(self._pdict(
                    {'line': m[0], 'match': code}))
            if key == 'table':
                found['table'].append(self._pdict(
                    {'line': m[0], 'match': code}))
            if key == 'group':
                found['group'].append(self._pdict(
                    {'line': m[0], 'match': code}))
            if key == 'groupEnd':
                found['groupEnd'].append(
                    self._pdict({'line': m[0], 'match': code}))
        self.log_debug('')
        return GreppedOut(found)
