import os
from . import config
from . import ui
from .runners import run_john_wordlist, run_john_only, write_hash_file,get_cracked_result,run_tracked_attack, run_mask_attack
from .identify import identify_hash
from .session import save_session, clear_session
from .passphrase import run_passphrase_attack


def crack_file_hash(john_hash_file, available_wl):
    """Crack a file-extracted hash using John the Ripper wordlists."""
    ui.section("Cracking File Hash with John the Ripper")
    for wl_name, wl_path in available_wl.items():
        result = run_john_wordlist(john_hash_file, wl_path, label=wl_name)
        if result:
            return result
    ui.warn("John wordlists exhausted — no result.")
    return None

def crack_raw_hash(hash_str, ctx, resume_session=None):
    if resume_session:
        hash_file = resume_session["hash_file"]
        mode = resume_session["mode"]
        completed = resume_session.get("completed_attacks", [])
        ui.info(f"Resuming from stage: {resume_session.get('stage', 'unknown')}")
        hash_name = next((n for n, m in config.HASH_MODE_MAP.items() if m == mode), "unknown")
    else:
        candidates, is_salted = identify_hash(hash_str)

        if len(candidates) == 1 and candidates[0][1] == 'john':
            return run_john_only(hash_str, ctx.available_wl)

        if is_salted:
            ui.warn("Salted hash — rainbow tables and quick online lookups won't work.")

        hash_file = write_hash_file(hash_str)
        hash_name, mode = candidates[0]
        completed = []
        save_session(hash_file, mode, "starting", completed)

    already = get_cracked_result(hash_file, mode)
    if already:
        ui.ok(f"Already cracked! Password: {already}")
        clear_session()
        return already

    rockyou      = ctx.available_wl.get("rockyou")
    crackstation = ctx.available_wl.get("crackstation")
    extras       = {k: v for k, v in ctx.available_wl.items() if k not in ("rockyou", "crackstation")}

    ui.section(f"Trying: {hash_name}  (mode {mode})")

    if mode in config.SLOW_MODES:
        ui.warn(f"{hash_name} is a slow/expensive algorithm — reduced attack plan")
        result = _attack_slow_hash(hash_file, mode, ctx.gpu_flags, ctx.patience_level, completed,
                                   rockyou, crackstation)
        if result:
            return result
    else:
        result = _attack_fast_hash(hash_file, mode, ctx.gpu_flags, ctx.patience_level, completed,
                                   rockyou, crackstation, extras)
        if result:
            return result

    # ── Passphrase combo (all hash modes) ───────────────────────────────
    if "passphrase" not in completed:
        max_pw = _passphrase_depth(mode, ctx)
        if max_pw > 0:
            result = run_passphrase_attack(hash_file, mode, ctx.gpu_flags, max_words=max_pw, patience=ctx.patience_level)
            completed.append("passphrase")
            save_session(hash_file, mode, "passphrase", completed)
            if result:
                return result

    return None

def _attack_slow_hash(hash_file, mode, gpu_flags,patience, completed,
                      rockyou, crackstation):
    """
    Attack plan for slow/expensive hash algorithms (bcrypt, scrypt, etc.).
    No extras — they'd take too long.

      quick(1):   rockyou straight only
      normal(2):  + rockyou + best64
      patient(3): + crackstation + best64
    """

    best64_path = config.RULE_PATHS.get("best64", "")
    has_best64 = os.path.exists(best64_path)

    plan = []

    if rockyou:
        plan.append((rockyou, None, "rockyou straight"))
        if patience >= 2 and has_best64:
            plan.append((rockyou, best64_path, "rockyou + best64"))

    if patience >= 3 and crackstation and has_best64:
        plan.append((crackstation, best64_path, "crackstation + best64"))

    for wordlist, rule, label in plan:
        result = run_tracked_attack(hash_file, mode, wordlist, gpu_flags,
                                    rule=rule, label=label,
                                    completed_attacks=completed)
        if result:
            return result

    return None

def _attack_fast_hash(hash_file, mode, gpu_flags,patience, completed,
                      rockyou, crackstation, extras):
    """
    Attack plan for fast hash algorithms (MD5, SHA1, SHA256, NTLM, etc.).

      quick(1):   rockyou x all rules
      normal(2):  + crackstation x all rules
      patient(3): + extras x all rules
    """

    all_rules = []
    for name in ("best64", "onetorule", "dive"):
        path = config.RULE_PATHS.get(name, "")
        if os.path.exists(path):
            all_rules.append((name, path))

    plan = []

    def add_wordlist(wl_path, wl_label, rules):
        """Add a wordlist straight, then with each rule in the list."""
        plan.append((wl_path,  f"{wl_label} straight",None))
        for rule_name, rule_path in rules:
            plan.append((wl_path,  f"{wl_label} + {rule_name}",rule_path,))

    # Level 1+: rockyou × all rules (always)
    if rockyou:
        add_wordlist(rockyou, "rockyou", all_rules)

    # Level 2+: crackstation × all rules
    if patience >= 2 and crackstation:
        add_wordlist(crackstation, "crackstation", all_rules)

    # Level 3: extras × all rules
    if patience >= 3:
        for wl_name, wl_path in extras.items():
            add_wordlist(wl_path, wl_name, all_rules)

    # ── Execute plan
    for wordlist, label, rule in plan:
        result = run_tracked_attack(hash_file, mode, wordlist, gpu_flags,
                                    rule=rule, label=label,
                                    completed_attacks=completed)
        if result:
            return result

    if mode in config.FAST_MODES:
        ui.section("Smart Mask Attacks")
        for level in range(1, patience + 1):
            for path in config.MASK_FILES[level]:
                full_path = f"{config.MASK_DIR}/{path}"
                if not os.path.exists(full_path):
                    continue
                attack_id = f"mask_{path}"
                if attack_id in completed:
                    continue
                ui.info(f"  Mask file: {path}")
                result = run_mask_attack(hash_file, mode, full_path, gpu_flags, label=path)
                completed.append(attack_id)
                save_session(hash_file, mode, "mask_attack", completed)
                if result:
                    return result

    return None

def _passphrase_depth(mode,ctx):
    """How many passphrase words to try, based on hash speed and patience."""
    if mode in config.FAST_MODES:
        return {"quick": 0, "normal": 2, "patient": 3}.get(ctx.patience, 2)
    else:
        return {"quick": 0, "normal": 1, "patient": 2}.get(ctx.patience, 1)
