import concurrent.futures
import requests
import json
import patoolib
import logging
import shutil

from bs4 import BeautifulSoup
from pathlib import Path
from util.config import Config
from util.file_manager import FileManager
from modules.create_backup import CreateBackup
from modules.fc_kks import FilterConvertKKS
from modules.install_chara import InstallChara
from modules.remove_chara import RemoveChara

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

CWD = Path.cwd()
TEST_PATH = CWD / "test"
GAMEPATH = TEST_PATH / "Koikatsu Party"
BACKUP_PATH = TEST_PATH / "Backup"
DOWNLOAD_PATH = TEST_PATH / "Downloads"


if TEST_PATH.exists() and TEST_PATH.is_dir():
    shutil.rmtree(TEST_PATH)

paths_to_check = [
    TEST_PATH,
    BACKUP_PATH,
    DOWNLOAD_PATH,
    GAMEPATH / "UserData",
    GAMEPATH / "BepInEx",
    GAMEPATH / "mods",
    GAMEPATH / "UserData" / "chara" / "female",
    GAMEPATH / "UserData" / "coordinate",
    GAMEPATH / "UserData" / "Overlays"
]

for path in paths_to_check:
    path.mkdir(parents=True, exist_ok=True)

def create_config():
    """Creates and saves the configuration file."""
    data = {
        "Core": {"GamePath": str(GAMEPATH)},
        "CreateBackup": {
            "Enable": True,
            "OutputPath": str(BACKUP_PATH),
            "BepInEx": False,
            "Filename": "koikatsu_backup",
            "mods": False,
            "UserData": False,
        },
        "FilterConvertKKS": {
            "Convert": True,
            "InputPath": str(DOWNLOAD_PATH),
            "Enable": True,
        },
        "InstallChara": {
            "Password": "Skip",
            "FileConflicts": "Skip",
            "Enable": True,
            "InputPath": str(DOWNLOAD_PATH),
        },
        "RemoveChara": {
            "Enable": True,
            "InputPath": str(DOWNLOAD_PATH),
        },
    }

    config_path = TEST_PATH / 'config/config.json'
    config_path.parent.mkdir(parents=True, exist_ok=True)
    with open(config_path, "w") as f:
        json.dump(data, f, indent=4)
    return Config(config_path)


CONFIG = create_config()
FILE_MANAGER = FileManager(CONFIG)


def valid_link(link_button):
    """Check if a link is a valid download button."""
    return link_button.text == 'Download' and link_button.get('href')


def get_card_urls(bepis_url: str) -> list[str]:
    """Extract card download URLs from a bepis page."""
    try:
        response = requests.get(bepis_url)
        response.raise_for_status()
    except requests.RequestException as e:
        logger.error(f"Error fetching {bepis_url}: {e}")
        return []

    soup = BeautifulSoup(response.text, 'html.parser')
    link_buttons = soup.find_all('a', class_='btn btn-primary btn-sm')
    return ['https://db.bepis.moe' + link_button.get('href') for link_button in link_buttons if valid_link(link_button)]


def download_card(card_url: str) -> Path:
    """Download a card from a given URL."""
    card_name = Path(card_url).name
    card_path = DOWNLOAD_PATH / card_name

    try:
        with requests.get(card_url, stream=True) as response:
            response.raise_for_status()
            with open(card_path, "wb") as out_file:
                out_file.write(response.content)
        logger.info(f"Downloaded {card_name}")
        return card_path
    except requests.RequestException as e:
        logger.error(f"Error downloading {card_url}: {e}")
        return None


def download(bepis_url: str):
    """Download all cards from a given bepis URL using a thread pool."""
    download_urls = get_card_urls(bepis_url)
    with concurrent.futures.ThreadPoolExecutor() as executor:
        results = list(executor.map(download_card, download_urls))

    return set(filter(None, results))


def test_fckss(kk_cards, kks_cards):
    """Test FilterConvertKKS functionality."""
    fckss = FilterConvertKKS(CONFIG, FILE_MANAGER)
    fckss.run()
    
    filter_path = DOWNLOAD_PATH / '_KKS_card_'
    conversion_path = DOWNLOAD_PATH / '_KKS_to_KK_'

    assert filter_path.exists(), "Filter path does not exist."
    assert conversion_path.exists(), "Conversion path does not exist."

    for kk_path in kk_cards:
        card_name = kk_path.name
        filtered_kks_path = filter_path / card_name
        converted_kks_path = conversion_path / f"KKS2KK_{card_name}"

        assert not filtered_kks_path.exists(), f"{filtered_kks_path} exists but should not."
        assert not converted_kks_path.exists(), f"{converted_kks_path} exists but should not."
    
    for kks_path in kks_cards:
        card_name = kks_path.name
        filtered_kks_path = filter_path / card_name
        converted_kks_path = conversion_path / f"KKS2KK_{card_name}"
        assert filtered_kks_path.exists(), f"{filtered_kks_path} does not exist."
        assert converted_kks_path.exists(), f"{converted_kks_path} does not exist."
        kk_cards.add(converted_kks_path)


def test_install(kk_cards):
    """Test character installation."""
    install_chara = InstallChara(CONFIG, FILE_MANAGER)
    install_chara.run()

    chara_path = GAMEPATH / 'UserData' / 'chara' / 'female'

    for card in kk_cards:
        assert (chara_path / card.name).exists(), f"{card.name} not found in installed cards."


def test_backup(kk_cards):
    """Test backup functionality."""
    create_backup = CreateBackup(CONFIG, FILE_MANAGER)
    create_backup.run()

    koikatsu_backup = BACKUP_PATH / 'koikatsu_backup.7z'
    assert koikatsu_backup.exists(), "Backup file does not exist."

    outdir = BACKUP_PATH / 'outdir'
    patoolib.extract_archive(str(koikatsu_backup), outdir=str(outdir))

    for card in kk_cards:
        assert (outdir / "UserData" / "chara" / "female" / card.name).exists(), f"{card.name} missing in backup."


def test_remove():
    """Test character removal."""
    remove_chara = RemoveChara(CONFIG, FILE_MANAGER)
    remove_chara.run()

    chara_path = GAMEPATH / 'UserData' / 'chara' / 'female'
    assert not list(chara_path.iterdir()), "Character directory not empty after removal."


def main():
    """Main function to run all tests."""
    kk_cards = download('https://db.bepis.moe/koikatsu?type=base')
    kks_cards = download('https://db.bepis.moe/koikatsu?type=sunshine')

    if kk_cards and kks_cards:
        test_fckss(kk_cards, kks_cards)
        test_install(kk_cards)
        test_backup(kk_cards)
        test_remove()
        shutil.rmtree(TEST_PATH)
    else:
        logger.error("No cards downloaded. Exiting.")


if __name__ == '__main__':
    main()
