"""Create Asset via an API Request."""
import json
from typing import Dict, Optional

from ostorlab.apis import request
from ostorlab.assets import asset as base_asset
from ostorlab.assets import android_aab
from ostorlab.assets import android_apk
from ostorlab.assets import file
from ostorlab.assets import ios_ipa
from ostorlab.assets import domain_name
from ostorlab.assets import ip
from ostorlab.assets import ipv4
from ostorlab.assets import ipv6


class CreateAssetAPIRequest(request.APIRequest):
    """Persist asset API request"""

    def __init__(self, asset: base_asset.Asset) -> None:
        """Constructs all the necessary attributes for the object."""
        self._asset = asset

    @property
    def query(self) -> Optional[str]:
        """Sets the query of the API request.

        Returns:
            The query to create the asset.
        """
        return """
            mutation CreateAsset($asset: AssetInputType!){
                createAsset(asset: $asset) {
                    asset{
                        ... on AndroidAssetType{
                            id
                        }
                        ... on IOSAssetType{
                            id
                        }
                        ... on AndroidFileAssetType{
                            id
                        }
                        ... on IOSFileAssetType{
                            id
                        }
                        ... on AndroidStoreAssetType{
                            id
                        }
                        ... on IOSStoreAssetType{
                            id
                        }
                        ... on UrlAssetType{
                            id
                        }
                        ... on DomainAssetType{
                            id
                        }
                        ... on SourceCodeAssetType{
                            id
                        }
                        ... on FileAssetType{
                            id
                        }
                        ... on IPAssetType{
                            id
                        }
                        ... on IPv4AssetType{
                            id
                        }
                        ... on IPv6AssetType{
                            id
                        }
                    }
                }
            }
        """

    @property
    def data(self) -> Optional[Dict]:
        """Sets the body of the API request.

        Returns:
            The body of the create asset request.
        """
        variables = {"asset": {"tags": []}}
        asset_type_variables = self.__get_asset_variables()
        variables["asset"].update(asset_type_variables)

        if any(
            isinstance(self._asset, t)
            for t in (
                android_aab.AndroidAab,
                android_apk.AndroidApk,
                file.File,
                ios_ipa.IOSIpa,
            )
        ):
            map_variables = self._get_map_variables()
            data = {
                "operations": json.dumps(
                    {"query": self.query, "variables": json.dumps(variables)}
                ),
                "map": json.dumps(map_variables),
            }
        else:
            data = {"query": self.query, "variables": json.dumps(variables)}
        return data

    @property
    def files(self) -> Optional[Dict]:
        """Sets the file for multipart upload of the API request.

        Returns:
            The file mapping of the create asset request.
        """
        if any(
            isinstance(self._asset, t)
            for t in (
                android_aab.AndroidAab,
                android_apk.AndroidApk,
                file.File,
                ios_ipa.IOSIpa,
            )
        ):
            return {"0": self._asset.content}
        else:
            return None

    def __get_asset_variables(self) -> Dict[str, Dict]:
        """Creates asset variables for the API request, depending on the type of the asset."""
        asset_type_variables = {}
        if isinstance(self._asset, (android_aab.AndroidAab, android_apk.AndroidApk)):
            asset_type_variables = {"androidFile": {"file": None}}
        elif isinstance(self._asset, ios_ipa.IOSIpa):
            asset_type_variables = {"iosFile": {"file": None}}
        elif isinstance(self._asset, file.File):
            asset_type_variables = {"file": {"content": None, "path": self._asset.path}}
        elif isinstance(self._asset, domain_name.DomainName):
            asset_type_variables = {"domain": {"domain": self._asset.name}}
        elif isinstance(self._asset, ip.IP):
            asset_type_variables = {
                "ip": {
                    "host": self._asset.host,
                    "mask": self._asset.mask,
                    "version": self._asset.version,
                }
            }
        elif isinstance(self._asset, ipv4.IPv4):
            asset_type_variables = {
                "ipV4": {
                    "host": self._asset.host,
                    "mask": self._asset.mask,
                    "version": self._asset.version,
                }
            }
        elif isinstance(self._asset, ipv6.IPv6):
            asset_type_variables = {
                "ipV6": {
                    "host": self._asset.host,
                    "mask": self._asset.mask,
                    "version": self._asset.version,
                }
            }
        else:
            raise NotImplementedError(f"Unknown asset type : {type(self._asset)}")

        return asset_type_variables

    def _get_map_variables(self):
        """Creates map variables for the API request, depending on the type of the asset."""
        map_variables = {}
        if isinstance(self._asset, (android_aab.AndroidAab, android_apk.AndroidApk)):
            map_variables = {"0": ["variables.asset.androidFile.file"]}
        elif isinstance(self._asset, ios_ipa.IOSIpa):
            map_variables = {"0": ["variables.asset.iosFile.file"]}
        elif isinstance(self._asset, file.File):
            map_variables = {"0": ["variables.asset.file.content"]}

        return map_variables
