#!/usr/bin/env python3

import asyncio
from json import dump, loads
from os import environ
from re import match

import aiohttp

from modules.export import export
from modules.subdomain_modules.alienvault_subs import alienvault
from modules.subdomain_modules.anubis_subs import anubisdb
from modules.subdomain_modules.bevigil_subs import bevigil
from modules.subdomain_modules.certspot_subs import certspot
from modules.subdomain_modules.chaos_subs import chaos
from modules.subdomain_modules.github_subs import github
from modules.subdomain_modules.htarget_subs import hackertgt
from modules.subdomain_modules.hunter_subs import hunter
from modules.subdomain_modules.leakix_subs import leakix
from modules.subdomain_modules.netlas_subs import netlas
from modules.subdomain_modules.shodan_subs import shodan
from modules.subdomain_modules.urlscan_subs import urlscan
from modules.subdomain_modules.virustotal_subs import virust
from modules.subdomain_modules.wayback_subs import machine
from modules.subdomain_modules.zoomeye_subs import zoomeye
from modules.write_log import log_writer

R = "\033[31m"  # red
G = "\033[32m"  # green
C = "\033[36m"  # cyan
W = "\033[0m"  # white
Y = "\033[33m"  # yellow
HEADER = "\033[1;35m"  # bold magenta

found = []


def migrate_config_file(config_path):
    current_schema = {
        "shodan": None,
        "zoomeye": None,
        "virustotal": None,
        "alienvault": None,
        "hunter": None,
        "chaos": None,
        "leakix": None,
        "netlas": None,
        "bevigil": None,
        "github": None,
    }

    try:
        with open(config_path, "r") as f:
            user_data = loads(f.read())
    except FileNotFoundError:
        user_data = {}

    missing_keys = {k: v for k, v in current_schema.items() if k not in user_data}

    if missing_keys:
        user_data.update(missing_keys)

        with open(config_path, "w") as f:
            dump(user_data, f, indent=4)
        print(
            f"[*] Migrated config: Added {len(missing_keys)} new API key slots to {config_path}"
        )

    return user_data


async def query(hostname, tout, api_keys):
    timeout = aiohttp.ClientTimeout(total=tout)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        results = await asyncio.gather(
            alienvault(hostname, api_keys["alienvault"], session),
            anubisdb(hostname, session),
            bevigil(hostname, api_keys["bevigil"], session),
            certspot(hostname, session),
            hackertgt(hostname, session),
            hunter(hostname, api_keys["hunter"], session),
            netlas(hostname, api_keys["netlas"], session),
            shodan(hostname, api_keys["shodan"], session),
            urlscan(hostname, session),
            virust(hostname, api_keys["virustotal"], session),
            zoomeye(hostname, api_keys["zoomeye"], session),
            chaos(hostname, api_keys["chaos"], session),
            leakix(hostname, api_keys["leakix"], session),
            github(hostname, api_keys["github"], session),
            machine(hostname, session),
        )
    await session.close()
    return results


def subdomains(hostname, tout, output, data, conf_path):
    global found
    result = {}
    api_keys = {}

    print(f"\n{HEADER}━━━ SubDomain Enum {'━' * 30}{W}\n")

    migrate_config_file(f"{conf_path}/keys.json")

    with open(f"{conf_path}/keys.json", "r") as keyfile:
        json_read = keyfile.read()

    keys_json = loads(json_read)
    api_keys["alienvault"] = environ.get("FR_ALIENVAULT_KEY") or keys_json.get(
        "alienvault"
    )
    api_keys["bevigil"] = environ.get("FR_BEVIGIL_KEY") or keys_json.get("bevigil")
    api_keys["hunter"] = environ.get("FR_HUNTER_KEY") or keys_json.get("hunter")
    api_keys["netlas"] = environ.get("FR_NETLAS_KEY") or keys_json.get("netlas")
    api_keys["shodan"] = environ.get("FR_SHODAN_KEY") or keys_json.get("shodan")
    api_keys["virustotal"] = environ.get("FR_VT_KEY") or keys_json.get("virustotal")
    api_keys["zoomeye"] = environ.get("FR_ZOOMEYE_KEY") or keys_json.get("zoomeye")
    api_keys["chaos"] = environ.get("FR_CHAOS_KEY") or keys_json.get("chaos")
    api_keys["leakix"] = environ.get("FR_LEAKIX_KEY") or keys_json.get("leakix")
    api_keys["github"] = environ.get("FR_GITHUB_KEY") or keys_json.get("github")

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    results = loop.run_until_complete(query(hostname, tout, api_keys))
    for name, subs, error in results:
        if error is None:
            found.extend(subs)
        if subs:
            print(f"{G}[+] {C}{name.ljust(15)}{W} : {len(subs)}")
        elif error is not None:
            if error.startswith("API key not configured"):
                print(f"{Y}[!] {C}{name.ljust(15)}{W} : {error}")
            else:
                print(f"{R}[-] {C}{name.ljust(15)}{W} : {error}")
    loop.close()

    found = [item for item in found if item.endswith(hostname)]
    valid = r"^[A-Za-z0-9._~()'!*:@,;+?-]*$"
    found = [item for item in found if match(valid, item)]
    found = set(found)
    total = len(found)

    if found:
        print(f"\n{G}[+]{W} {'Total Unique'.ljust(15)} : {total}\n")
        for url in enumerate(list(found)[:20]):
            print(url[1])

        if len(found) > 20:
            print(f"\n{C}[*]{W} Results truncated...")

    if output != "None":
        result["Links"] = list(found)
        result.update({"exported": False})
        data["module-Subdomain Enumeration"] = result
        fname = f"{output['directory']}/subdomains.{output['format']}"
        output["file"] = fname
        export(output, data)
    log_writer("[subdom] Completed")
