#!/usr/bin/env python3
import argparse
import itertools
import math
import os
import sys

NEWLINE_BYTES = 1  # '\n'

BANNER = r"""
██████╗ ██╗      ██████╗  ██████╗ ██████╗ ██╗     ██╗███╗   ██╗███████╗
██╔══██╗██║     ██╔═══██╗██╔═══██╗██╔══██╗██║     ██║████╗  ██║██╔════╝
██████╔╝██║     ██║   ██║██║   ██║██║  ██║██║     ██║██╔██╗ ██║█████╗  
██╔══██╗██║     ██║   ██║██║   ██║██║  ██║██║     ██║██║╚██╗██║██╔══╝  
██████╔╝███████╗╚██████╔╝╚██████╔╝██████╔╝███████╗██║██║ ╚████║███████╗
╚═════╝ ╚══════╝ ╚═════╝  ╚═════╝ ╚═════╝ ╚══════╝╚═╝╚═╝  ╚═══╝╚══════╝

BLOODLINE-PASSWORD-GENERATOR
AUTHOR Raed38 
"""


def parse_words(source: str):
    if os.path.isfile(source):
        with open(source, 'r', encoding='utf-8') as f:
            raw = f.read()
    else:
        raw = source

    words = [w.strip() for w in raw.replace(',', '\n').split('\n') if w.strip()]

    if not words:
        raise ValueError("No words provided")

    return words


def exact_estimate(words, min_r=1, max_r=None):
    n = len(words)
    if max_r is None:
        max_r = n

    if not (1 <= min_r <= max_r <= n):
        raise ValueError("Invalid min/max values")

    byte_lengths = [len(w.encode('utf-8')) for w in words]
    sum_bytes = sum(byte_lengths)

    total_lines = 0
    total_bytes = 0

    for r in range(min_r, max_r + 1):
        perms = math.factorial(n) // math.factorial(n - r)
        total_lines += perms

        appearances_per_word = r * (math.factorial(n - 1) // math.factorial(n - r))
        words_bytes = appearances_per_word * sum_bytes

        total_bytes += words_bytes + (perms * NEWLINE_BYTES)

    return total_lines, total_bytes


def confirm_or_exit(lines, bytes_):
    print("\n WARNING ")
    print("You are about to generate a wordlist with:")
    print(f"  Lines : {lines:,}")
    print(f"  Size  : {bytes_ / (1024 * 1024):.2f} MB")
    print("BE CAREFUL TO NOT CRASH YOUR SYSTEM :)")

    resp = input("Type YES to continue: ")
    if resp.strip().lower() not in ("y", "yes"):
        print("Aborted by user.")
        sys.exit(0)


def generate(words, min_r, max_r, output):
    with open(output, 'w', encoding='utf-8') as f:
        for r in range(min_r, max_r + 1):
            for perm in itertools.permutations(words, r):
                f.write(''.join(perm) + '\n')


def main():
    print(BANNER)

    ap = argparse.ArgumentParser(
        description="BLOODLINE - prediction-first wordlist generator"
    )

    ap.add_argument('-i', '--input', required=True,
                    help='Comma-separated words, multiline string, or file path')
    ap.add_argument('--min', dest='min_r', type=int, default=1,
                    help='Minimum permutation length')
    ap.add_argument('--max', dest='max_r', type=int, default=None,
                    help='Maximum permutation length')
    ap.add_argument('--estimate', action='store_true',
                    help='Show exact size estimation only')
    ap.add_argument('--generate', action='store_true',
                    help='Generate the wordlist')
    ap.add_argument('-o', '--output', default='wordlist.txt',
                    help='Output file name')

    args = ap.parse_args()

    try:
        words = parse_words(args.input)
    except ValueError as e:
        print(f"Error: {e}")
        sys.exit(1)

    max_r = args.max_r if args.max_r is not None else len(words)

    lines, bytes_ = exact_estimate(words, args.min_r, max_r)

    if args.estimate:
        print(f"LINES: {lines:,}")
        print(f"BYTES: {bytes_:,}")
        print(f"MB: {bytes_ / (1024 * 1024):.6f}")

    if args.generate:
        confirm_or_exit(lines, bytes_)
        generate(words, args.min_r, max_r, args.output)
        print("DONE")


if __name__ == '__main__':
    main()
