"""
disk.py — Disk space analysis functions.
"""

import os
import subprocess
from pathlib import Path

from modules.output import good, bad, warn, info, header
from modules.utils import run, require_bin, term_width


def tops(args=None):
    """
    Show the top largest files and directories.

    Usage:
        tops                    — scan all /home/* and /backup/
        tops -d <directory>     — scan a specific directory
        tops <N>                — top N results (default 10)
        tops <N> -d <directory> — top N in specific directory
        tops -h                 — show help
    """
    args = args or []

    if '-h' in args:
        _tops_usage()
        return

    num = 10
    directory = None

    # Parse: tops -d <dir>
    if args and args[0] == '-d':
        if len(args) < 2:
            _tops_usage()
            return
        directory = args[1]
        _show_biggest(num, directory)
        return

    # Parse: tops <N> [-d <dir>]
    if args:
        try:
            num = int(args[0])
            args = args[1:]
        except ValueError:
            # Treat as a directory path if it looks like one
            if args[0].startswith('/') or os.path.isdir(args[0]):
                _show_biggest(num, args[0])
                return

    if len(args) >= 2 and args[0] == '-d':
        directory = args[1]
        _show_biggest(num, directory)
        return

    # Default: scan all user dirs + /backup
    skip = {'virtfs', 'csf'}
    home = Path('/home')
    if home.is_dir():
        for user_dir in sorted(home.iterdir()):
            if user_dir.is_dir() and user_dir.name not in skip:
                _show_biggest(num, str(user_dir))

    _show_biggest(num, '/backup')


def _show_biggest(count, directory):
    if not os.path.isdir(directory):
        warn(f"Directory not found: {directory}")
        return
    print(f"\nTop {count} biggest files and directories in {directory}:")
    out, _, _ = run(
        ['du', '-ah', '--threshold=100M', '--max-depth=1', directory]
    )
    lines = out.strip().splitlines()
    # Sort by size (human-readable sort via sort -rh equivalent)
    # We re-sort by parsing the size field
    parsed = []
    for line in lines:
        parts = line.split('\t', 1)
        if len(parts) == 2:
            parsed.append((parts[0], parts[1]))

    path_w = max(55, term_width() - 12)
    parsed.sort(key=lambda x: _human_to_bytes(x[0]), reverse=True)
    for size, path in parsed[:count]:
        print(f"  {path:<{path_w}} {size}")
    print()


def _human_to_bytes(size_str):
    """Convert human-readable size (e.g. '1.5G') to bytes for sorting."""
    size_str = size_str.strip().upper()
    units = {'K': 1024, 'M': 1024**2, 'G': 1024**3, 'T': 1024**4}
    if size_str[-1] in units:
        try:
            return float(size_str[:-1]) * units[size_str[-1]]
        except ValueError:
            return 0
    try:
        return float(size_str)
    except ValueError:
        return 0


def _tops_usage():
    print("Usage: tops [N] [-d <directory>]")
    print("  -h          Show this help")
    print("  -d <dir>    Scan specific directory (default N=10)")
    print("  N           Number of results (default 10)")
    print("  N -d <dir>  Top N results in specific directory")


def finddiskspace():
    """
    Check disk usage. Warn if less than 15% free. List large files/backups.
    """
    out, _, _ = run(['df', '-h'])
    root_pct = 0
    root_used = root_total = '?'
    for line in out.splitlines()[1:]:
        parts = line.split()
        if len(parts) >= 6 and parts[5] == '/':
            root_pct = int(parts[4].rstrip('%'))
            root_used = parts[2]
            root_total = parts[1]
            break

    free_pct = 100 - root_pct
    if free_pct < 15:
        bad("Warning! Less than 15% disk space remaining!")
        print(f"Disk Usage: {root_used}/{root_total} ({root_pct}% used, {free_pct}% free)\n")

        import socket
        print(f"Searching for files larger than 250M on {socket.getfqdn()}:\n")

        # Backup files — list individually then total
        backup_files = []
        if os.path.isdir('/backup'):
            find_out, _, _ = run(['find', '/backup', '-size', '+250M',
                                  '-not', '-path', '*/virtfs/*', '-print'])
            backup_files = [f for f in find_out.strip().splitlines() if f]
        print("Backups:")
        if backup_files:
            out2, _, _ = run(['du', '-shc'] + backup_files)
            for line in out2.splitlines():
                print(line)
        else:
            info("No files >250M found in /backup.")

        # Large files in /home (exclude virtfs)
        print("\nFile Usage (files > 250M in /home):")
        find_out, _, _ = run(['find', '/home', '-size', '+250M',
                               '-not', '-path', '*/virtfs/*', '-print'])
        home_files = [f for f in find_out.strip().splitlines() if f]
        if home_files:
            out2, _, _ = run(['du', '-shc'] + home_files)
            for line in out2.splitlines():
                print(line)
        else:
            info("No files >250M found in /home.")

        # Combined total
        all_files = backup_files + home_files
        if all_files:
            print("\nTotal Usage (Files + Backup):")
            out3, _, _ = run(['du', '-shc'] + all_files)
            for line in out3.splitlines():
                if 'total' in line:
                    print(line)
    else:
        print()
        info(f"Disk space OK — {free_pct}% free")
        print(f"Disk Usage: {root_used}/{root_total} ({root_pct}% used)\n")


def run_passthrough(cmd):
    """Run a command with output directly to stdout."""
    try:
        subprocess.run(cmd, check=False)
    except FileNotFoundError:
        warn(f"Command not found: {cmd[0]}")


def dspace():
    """
    Deep disk analysis: find directories and files over 200MB in /home and /backup.
    Pure Python 3 rewrite of the original embedded Python 2 script.
    """
    import os

    THRESHOLD_MB = 200
    THRESHOLD_BYTES = THRESHOLD_MB * 1024 * 1024
    paths_to_drill = []

    print(f"Looking for directories over {THRESHOLD_MB}MB")
    print(f"Checking for large directories within /backup/ /home/")

    def get_dir_size_bytes(path):
        """Get directory size in bytes using du -s."""
        out, _, _ = run(['du', '-s', '--block-size=1', path])
        parts = out.strip().split()
        if parts:
            try:
                return int(parts[0])
            except ValueError:
                pass
        return 0

    def format_size(size_bytes):
        if size_bytes >= 1024 ** 3:
            return f"{size_bytes / 1024**3:.2f}G"
        elif size_bytes >= 1024 ** 2:
            return f"{size_bytes / 1024**2:.0f}M"
        else:
            return f"{size_bytes / 1024:.0f}K"

    def check_dir(base_path):
        if not os.path.isdir(base_path):
            print(f"{base_path} is empty or does not exist")
            return
        if not os.listdir(base_path):
            print(f"{base_path} is empty")
            return

        try:
            entries = sorted(os.scandir(base_path), key=lambda e: e.name)
        except PermissionError:
            warn(f"Permission denied: {base_path}")
            return

        for entry in entries:
            if not entry.is_dir(follow_symlinks=False):
                continue
            if entry.name in ('virtfs',):
                continue

            size = get_dir_size_bytes(entry.path)
            if size >= THRESHOLD_BYTES:
                depth = entry.path.count('/')
                if depth >= 4:
                    print(f"{format_size(size):<10} {entry.path}")
                else:
                    paths_to_drill.append(entry.path)

    check_dir('/backup/')
    check_dir('/home/')

    # Drill into top-level dirs that were too shallow
    for p in paths_to_drill:
        check_dir(p)


def findbigdirs():
    """
    Find directories with 800+ files anywhere under /home/* and /tmp.
    """
    search_paths = ['/tmp']
    home = Path('/home')
    if home.is_dir():
        for user_dir in home.iterdir():
            if user_dir.is_dir() and user_dir.name != 'virtfs':
                search_paths.append(str(user_dir))

    from collections import Counter
    dir_counts = Counter()

    for search_path in search_paths:
        try:
            result = subprocess.run(
                ['find', search_path, '-xdev', '-printf', '%h\n'],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )
            for line in result.stdout.decode('utf-8', errors='replace').splitlines():
                dir_counts[line] += 1
        except Exception as e:
            warn(f"Error scanning {search_path}: {e}")

    header(f"{'Count':<8} Directory")
    print("-" * 60)
    results = [(d, c) for d, c in dir_counts.items() if c >= 800]
    results.sort(key=lambda x: x[1], reverse=True)
    if results:
        for d, c in results:
            print(f"{c:<8} {d}")
    else:
        info("No directories with 800+ files found.")
