# -*- coding: utf_8 -*-
"""Module for iOS App Plist Analysis."""

import logging
import os
from plistlib import (
    dumps,
    load,
)
from re import sub

from biplist import (
    InvalidPlistException,
    readPlist,
    writePlistToString,
)

from mobsf.MobSF.utils import is_file_exists
from mobsf.StaticAnalyzer.views.ios.permission_analysis import (
    check_permissions,
)
from mobsf.StaticAnalyzer.views.ios.app_transport_security import (
    check_transport_security,
)
from mobsf.StaticAnalyzer.views.shared_func import (
    is_secret,
)

logger = logging.getLogger(__name__)


def convert_bin_xml(bin_xml_file):
    """Convert Binary XML to Readable XML."""
    try:
        plist_obj = readPlist(bin_xml_file)
        data = writePlistToString(plist_obj)
        return data
    except InvalidPlistException:
        logger.warning('Failed to convert plist')


def plist_analysis(src, is_source):
    """Plist Analysis."""
    try:
        logger.info('iOS Info.plist Analysis Started')
        plist_info = {
            'bin_name': '',
            'bin': '',
            'id': '',
            'version': '',
            'build': '',
            'sdk': '',
            'pltfm': '',
            'min': '',
            'plist_xml': '',
            'permissions': {},
            'inseccon': [],
            'bundle_name': '',
            'build_version_name': '',
            'bundle_url_types': [],
            'bundle_supported_platforms': [],
            'bundle_version_name': '',
        }
        plist_file = None
        plist_files = []
        if is_source:
            logger.info('Finding Info.plist in iOS Source')
            for dirpath, _dirnames, files in os.walk(src):
                for name in files:
                    if (not any(x in dirpath for x in ['__MACOSX', 'Pods'])
                            and name.endswith('.plist')):
                        plist_files.append(os.path.join(dirpath, name))
                        if name == 'Info.plist':
                            plist_file = os.path.join(dirpath, name)
        else:
            logger.info('Finding Info.plist in iOS Binary')
            dirs = os.listdir(src)
            dot_app_dir = ''
            for dir_ in dirs:
                if dir_.endswith('.app'):
                    dot_app_dir = dir_
                    break
            bin_dir = os.path.join(src, dot_app_dir)  # Full Dir/Payload/x.app
            plist_file = os.path.join(bin_dir, 'Info.plist')
            plist_files = [plist_file]

        # Skip Plist Analysis if there is no Info.plist
        if not plist_file or not is_file_exists(plist_file):
            logger.warning(
                'Cannot find Info.plist file. Skipping Plist Analysis.')
            return plist_info

        # Generic Plist Analysis
        plist_obj = {}
        with open(plist_file, 'rb') as fp:
            plist_obj = load(fp)
        plist_info['plist_xml'] = dumps(
            plist_obj).decode('utf-8', 'ignore')
        plist_info['bin_name'] = (plist_obj.get('CFBundleDisplayName', '')
                                  or plist_obj.get('CFBundleName', ''))
        if not plist_info['bin_name'] and not is_source:
            # For iOS IPA
            plist_info['bin_name'] = dot_app_dir.replace('.app', '')
        plist_info['bin'] = plist_obj.get('CFBundleExecutable', '')
        plist_info['id'] = plist_obj.get('CFBundleIdentifier', '')
        plist_info['build'] = plist_obj.get('CFBundleVersion', '')
        plist_info['sdk'] = plist_obj.get('DTSDKName', '')
        plist_info['pltfm'] = plist_obj.get('DTPlatformVersion', '')
        plist_info['min'] = plist_obj.get('MinimumOSVersion', '')
        plist_info['bundle_name'] = plist_obj.get('CFBundleName', '')
        plist_info['bundle_version_name'] = plist_obj.get(
            'CFBundleShortVersionString', '')
        plist_info['bundle_url_types'] = plist_obj.get(
            'CFBundleURLTypes', [])
        plist_info['bundle_supported_platforms'] = plist_obj.get(
            'CFBundleSupportedPlatforms', [])
        logger.info('Checking Permissions')
        logger.info('Checking for Insecure Connections')
        for plist_file_ in plist_files:
            plist_obj_ = {}
            with open(plist_file_, 'rb') as fp:
                plist_obj_ = load(fp)
            # Check for app-permissions
            plist_info['permissions'].update(check_permissions(plist_obj_))
            # Check for ats misconfigurations
            plist_info['inseccon'] += check_transport_security(plist_obj_)
        return plist_info
    except Exception:
        logger.exception('Reading from Info.plist')


def get_plist_secrets(xml_string):
    """Get possible hardcoded secrets from plist."""
    result_list = []

    def _remove_tags(data):
        """Remove tags from input."""
        return sub('<[^<]+>', '', data).strip()

    xml_list = xml_string.split('\n')

    for index, line in enumerate(xml_list):
        if '<key>' in line and is_secret(_remove_tags(line)):
            nxt = index + 1
            value = (
                _remove_tags(xml_list[nxt])if nxt < len(xml_list) else False)
            if value:
                result_list.append(
                    f'{_remove_tags(line)} : {_remove_tags(value)}')
    return result_list
