import pytest
import random
import string
import re
from sherlock_project.sherlock import sherlock
from sherlock_project.notify import QueryNotify
from sherlock_project.result import QueryStatus
#from sherlock_interactives import Interactives


def simple_query(sites_info: dict, site: str, username: str) -> QueryStatus:
    query_notify = QueryNotify()
    site_data: dict = {}
    site_data[site] = sites_info[site]
    return sherlock(
        username=username,
        site_data=site_data,
        query_notify=query_notify,
    )[site]['status'].status


@pytest.mark.online
class TestLiveTargets:
    """Actively test probes against live and trusted targets"""
    # Known positives should only use sites trusted to be reliable and unchanging
    @pytest.mark.parametrize('site,username',[
        ('GitLab', 'ppfeister'),
        ('AllMyLinks', 'blue'),
    ])
    def test_known_positives_via_message(self, sites_info, site, username):
        assert simple_query(sites_info=sites_info, site=site, username=username) is QueryStatus.CLAIMED


    # Known positives should only use sites trusted to be reliable and unchanging
    @pytest.mark.parametrize('site,username',[
        ('GitHub', 'ppfeister'),
        ('GitHub', 'sherlock-project'),
        ('Docker Hub', 'ppfeister'),
        ('Docker Hub', 'sherlock'),
    ])
    def test_known_positives_via_status_code(self, sites_info, site, username):
        assert simple_query(sites_info=sites_info, site=site, username=username) is QueryStatus.CLAIMED


    # Known positives should only use sites trusted to be reliable and unchanging
    @pytest.mark.parametrize('site,username',[
        ('BodyBuilding', 'blue'),
        ('devRant', 'blue'),
    ])
    def test_known_positives_via_response_url(self, sites_info, site, username):
        assert simple_query(sites_info=sites_info, site=site, username=username) is QueryStatus.CLAIMED


    # Randomly generate usernames of high length and test for positive availability
    # Randomly generated usernames should be simple alnum for simplicity and high
    # compatibility. Several attempts may be made ~just in case~ a real username is
    # generated.
    @pytest.mark.parametrize('site,random_len',[
        ('GitLab', 255),
        ('Codecademy', 30)
    ])
    def test_likely_negatives_via_message(self, sites_info, site, random_len):
        num_attempts: int = 3
        attempted_usernames: list[str] = []
        status: QueryStatus = QueryStatus.CLAIMED
        for i in range(num_attempts):
            acceptable_types = string.ascii_letters + string.digits
            random_handle = ''.join(random.choice(acceptable_types) for _ in range (random_len))
            attempted_usernames.append(random_handle)
            status = simple_query(sites_info=sites_info, site=site, username=random_handle)
            if status is QueryStatus.AVAILABLE:
                break
        assert status is QueryStatus.AVAILABLE, f"Could not validate available username after {num_attempts} attempts with randomly generated usernames {attempted_usernames}."


    # Randomly generate usernames of high length and test for positive availability
    # Randomly generated usernames should be simple alnum for simplicity and high
    # compatibility. Several attempts may be made ~just in case~ a real username is
    # generated.
    @pytest.mark.parametrize('site,random_len',[
        ('GitHub', 39),
        ('Docker Hub', 30)
    ])
    def test_likely_negatives_via_status_code(self, sites_info, site, random_len):
        num_attempts: int = 3
        attempted_usernames: list[str] = []
        status: QueryStatus = QueryStatus.CLAIMED
        for i in range(num_attempts):
            acceptable_types = string.ascii_letters + string.digits
            random_handle = ''.join(random.choice(acceptable_types) for _ in range (random_len))
            attempted_usernames.append(random_handle)
            status = simple_query(sites_info=sites_info, site=site, username=random_handle)
            if status is QueryStatus.AVAILABLE:
                break
        assert status is QueryStatus.AVAILABLE, f"Could not validate available username after {num_attempts} attempts with randomly generated usernames {attempted_usernames}."


def test_username_illegal_regex(sites_info):
    site: str = 'BitBucket'
    invalid_handle: str = '*#$Y&*JRE'
    pattern = re.compile(sites_info[site]['regexCheck'])
    # Ensure that the username actually fails regex before testing sherlock
    assert pattern.match(invalid_handle) is None
    assert simple_query(sites_info=sites_info, site=site, username=invalid_handle) is QueryStatus.ILLEGAL