#!/usr/bin/env python3
"""
zee-clone - A professional website cloning tool
Clones websites by downloading HTML, CSS, JavaScript, images, fonts, videos, and more.
Version: 1.0.5
"""

import os
import sys
import argparse
import requests
from urllib.parse import urlparse, urljoin, unquote
from bs4 import BeautifulSoup
from pathlib import Path
from typing import Set, Tuple, Dict, List
import logging
from datetime import datetime
import time
import re
import hashlib
from collections import defaultdict

# Configure logging with colors
class ColoredFormatter(logging.Formatter):
    """Custom formatter with colors"""
    grey = "\x1b[38;20m"
    blue = "\x1b[34;20m"
    yellow = "\x1b[33;20m"
    red = "\x1b[31;20m"
    green = "\x1b[32;20m"
    cyan = "\x1b[36;20m"
    magenta = "\x1b[35;20m"
    bold = "\x1b[1m"
    reset = "\x1b[0m"
    
    format_str = "%(message)s"
    
    FORMATS = {
        logging.DEBUG: grey + format_str + reset,
        logging.INFO: blue + format_str + reset,
        logging.WARNING: yellow + format_str + reset,
        logging.ERROR: red + format_str + reset,
        'SUCCESS': green + format_str + reset,
        'HEADER': cyan + bold + format_str + reset,
        'PROGRESS': magenta + format_str + reset
    }

    def format(self, record):
        if hasattr(record, 'custom_level'):
            log_fmt = self.FORMATS.get(record.custom_level, self.FORMATS[logging.INFO])
        else:
            log_fmt = self.FORMATS.get(record.levelno, self.FORMATS[logging.INFO])
        
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Add console handler with colored formatter
console_handler = logging.StreamHandler()
console_handler.setFormatter(ColoredFormatter())
logger.addHandler(console_handler)

# Custom log levels
SUCCESS = 25
PROGRESS = 15
HEADER = 35
logging.addLevelName(SUCCESS, "SUCCESS")
logging.addLevelName(PROGRESS, "PROGRESS")
logging.addLevelName(HEADER, "HEADER")


def show_banner():
    """Display a beautiful ASCII art banner"""
    banner = f"""
{ColoredFormatter.bold}{ColoredFormatter.cyan}╔══════════════════════════════════════════════════════════╗
║                                                          ║
║   ███████╗███████╗███████╗      ██████╗██╗      ██████╗ ███╗   ██╗███████╗
║   ╚══███╔╝██╔════╝██╔════╝     ██╔════╝██║     ██╔═══██╗████╗  ██║██╔════╝
║     ███╔╝ █████╗  █████╗       ██║     ██║     ██║   ██║██╔██╗ ██║█████╗  
║    ███╔╝  ██╔══╝  ██╔══╝       ██║     ██║     ██║   ██║██║╚██╗██║██╔══╝  
║   ███████╗███████╗███████╗     ╚██████╗███████╗╚██████╔╝██║ ╚████║███████╗
║   ╚══════╝╚══════╝╚══════╝      ╚═════╝╚══════╝ ╚═════╝ ╚═╝  ╚═══╝╚══════╝
║                                                          ║
║                    Website Cloning Tool                  ║
║                    Version 1.0.5 | MIT License          ║
║                    Enhanced Edition                      ║
╚══════════════════════════════════════════════════════════╝{ColoredFormatter.reset}
    """
    print(banner)


def show_usage():
    """Display usage instructions"""
    usage = f"""
{ColoredFormatter.bold}{ColoredFormatter.yellow}📋 USAGE:{ColoredFormatter.reset}
    {ColoredFormatter.green}zee-clone <URL>{ColoredFormatter.reset}                    - Clone a website
    {ColoredFormatter.green}zee-clone <URL> -o <directory>{ColoredFormatter.reset}    - Clone to specific directory
    {ColoredFormatter.green}zee-clone <URL> -d 2{ColoredFormatter.reset}               - Download depth (follow links)
    {ColoredFormatter.green}zee-clone <URL> --all{ColoredFormatter.reset}              - Download all resources (aggressive mode)
    {ColoredFormatter.green}zee-clone --help{ColoredFormatter.reset}                   - Show this help message

{ColoredFormatter.bold}{ColoredFormatter.yellow}🎯 EXAMPLES:{ColoredFormatter.reset}
    {ColoredFormatter.cyan}$ zee-clone https://example.com{ColoredFormatter.reset}
    {ColoredFormatter.cyan}$ zee-clone https://example.com -d 2{ColoredFormatter.reset} (follow links)
    {ColoredFormatter.cyan}$ zee-clone https://example.com --all{ColoredFormatter.reset} (aggressive mode)
    {ColoredFormatter.cyan}$ zee-clone https://example.com -v{ColoredFormatter.reset}  (verbose mode)

{ColoredFormatter.bold}{ColoredFormatter.yellow}⚡ FEATURES:{ColoredFormatter.reset}
    • Downloads HTML, CSS, JavaScript, images, fonts, videos, and more
    • Handles inline CSS and JavaScript resources
    • Extracts resources from style attributes and @import statements
    • Follows @font-face declarations
    • Downloads resources from background images
    • Handles relative URLs automatically
    • Creates organized folder structure
    • Progress indicators for all downloads
    • Beautiful colored output
    """
    print(usage)


def create_folder_name(url: str) -> str:
    """
    Create a folder name from the website domain.
    
    Args:
        url: The website URL
    
    Returns:
        A sanitized folder name with dots replaced by underscores
    """
    parsed_url = urlparse(url)
    domain = parsed_url.netloc or parsed_url.path.split('/')[0]
    # Replace dots with underscores and remove any special characters
    folder_name = domain.replace('.', '_').replace('/', '_')
    return folder_name


def get_file_extension(url: str, content_type: str = "") -> str:
    """
    Determine file extension from URL or content type.
    
    Args:
        url: The file URL
        content_type: HTTP content-type header
    
    Returns:
        Appropriate file extension
    """
    # Try to get extension from URL
    parsed = urlparse(url)
    path = unquote(parsed.path)
    ext = os.path.splitext(path)[1].lower()
    
    if ext and len(ext) <= 6:  # Valid extension
        return ext
    
    # Fallback to content-type mapping
    content_type_map = {
        'image/jpeg': '.jpg',
        'image/jpg': '.jpg',
        'image/png': '.png',
        'image/gif': '.gif',
        'image/webp': '.webp',
        'image/svg+xml': '.svg',
        'image/x-icon': '.ico',
        'text/css': '.css',
        'application/javascript': '.js',
        'application/x-javascript': '.js',
        'text/javascript': '.js',
        'font/woff': '.woff',
        'font/woff2': '.woff2',
        'font/ttf': '.ttf',
        'font/otf': '.otf',
        'application/vnd.ms-fontobject': '.eot'
    }
    
    return content_type_map.get(content_type.split(';')[0], '') or '.bin'


def extract_css_resources(css_content: str, base_url: str) -> Set[str]:
    """
    Extract URLs from CSS content.
    
    Args:
        css_content: CSS text content
        base_url: Base URL for resolving relative paths
    
    Returns:
        Set of resource URLs found in CSS
    """
    resources = set()
    
    # Find url() patterns
    url_pattern = r'url\([\'"]?([^\'")]+)[\'"]?\)'
    urls = re.findall(url_pattern, css_content)
    
    for url in urls:
        if url and not url.startswith('data:'):
            full_url = urljoin(base_url, url.strip())
            resources.add(full_url)
    
    # Find @import statements
    import_pattern = r'@import\s+[\'"]?([^\'";]+)[\'"]?'
    imports = re.findall(import_pattern, css_content)
    
    for imp in imports:
        if imp and not imp.startswith('data:'):
            full_url = urljoin(base_url, imp.strip())
            resources.add(full_url)
    
    return resources


def get_all_resources(soup: BeautifulSoup, base_url: str, css_content: Dict[str, str]) -> Dict[str, Set[str]]:
    """
    Extract all possible resources from HTML and CSS.
    
    Args:
        soup: BeautifulSoup object of the HTML
        base_url: Base URL for resolving paths
        css_content: Dictionary mapping CSS URLs to their content
    
    Returns:
        Dictionary of resource types and their URLs
    """
    resources = {
        'images': set(),
        'css': set(),
        'js': set(),
        'fonts': set(),
        'video': set(),
        'audio': set(),
        'iframe': set(),
        'other': set()
    }
    
    # Find all image tags (including srcset)
    for img in soup.find_all('img'):
        # Regular src attribute
        if img.get('src'):
            src = img['src']
            if src and not src.startswith('data:'):
                full_url = urljoin(base_url, src)
                resources['images'].add(full_url)
        
        # srcset attribute for responsive images
        if img.get('srcset'):
            srcset = img['srcset']
            for part in srcset.split(','):
                src = part.strip().split()[0]
                if src and not src.startswith('data:'):
                    full_url = urljoin(base_url, src)
                    resources['images'].add(full_url)
    
    # Find all CSS links
    for link in soup.find_all('link'):
        if link.get('rel') and 'stylesheet' in link.get('rel', []):
            if link.get('href'):
                href = link['href']
                if href:
                    full_url = urljoin(base_url, href)
                    resources['css'].add(full_url)
        
        # Preload links
        if link.get('as') == 'style' and link.get('href'):
            full_url = urljoin(base_url, link['href'])
            resources['css'].add(full_url)
        
        # Icon links
        if link.get('rel') and any(r in ['icon', 'apple-touch-icon'] for r in link.get('rel', [])):
            if link.get('href'):
                full_url = urljoin(base_url, link['href'])
                resources['images'].add(full_url)
    
    # Find all JavaScript files
    for script in soup.find_all('script'):
        if script.get('src'):
            src = script['src']
            if src and not src.startswith('data:'):
                full_url = urljoin(base_url, src)
                resources['js'].add(full_url)
    
    # Find video sources
    for video in soup.find_all('video'):
        # src attribute
        if video.get('src'):
            full_url = urljoin(base_url, video['src'])
            resources['video'].add(full_url)
        
        # source tags inside video
        for source in video.find_all('source'):
            if source.get('src'):
                full_url = urljoin(base_url, source['src'])
                resources['video'].add(full_url)
    
    # Find audio sources
    for audio in soup.find_all('audio'):
        if audio.get('src'):
            full_url = urljoin(base_url, audio['src'])
            resources['audio'].add(full_url)
        
        for source in audio.find_all('source'):
            if source.get('src'):
                full_url = urljoin(base_url, source['src'])
                resources['audio'].add(full_url)
    
    # Find iframe sources
    for iframe in soup.find_all('iframe'):
        if iframe.get('src'):
            src = iframe['src']
            if src and not src.startswith('data:'):
                full_url = urljoin(base_url, src)
                resources['iframe'].add(full_url)
    
    # Find inline style attributes
    for tag in soup.find_all(style=True):
        style = tag['style']
        style_urls = re.findall(r'url\([\'"]?([^\'")]+)[\'"]?\)', style)
        for url in style_urls:
            if url and not url.startswith('data:'):
                full_url = urljoin(base_url, url)
                resources['images'].add(full_url)
    
    # Find meta tags with content (like og:image)
    for meta in soup.find_all('meta'):
        if meta.get('property', '').startswith('og:') or meta.get('name', '').startswith('twitter:'):
            if meta.get('content') and ('image' in meta.get('property', '') or 'image' in meta.get('name', '')):
                content = meta['content']
                if content.startswith(('http://', 'https://')):
                    resources['images'].add(content)
    
    # Extract resources from CSS
    for css_url, css_text in css_content.items():
        css_resources = extract_css_resources(css_text, base_url)
        for resource in css_resources:
            # Determine resource type from extension
            ext = get_file_extension(resource).lower()
            if ext in ['.woff', '.woff2', '.ttf', '.otf', '.eot']:
                resources['fonts'].add(resource)
            elif ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico']:
                resources['images'].add(resource)
            else:
                resources['other'].add(resource)
    
    return resources


def download_file(url: str, output_path: Path, session: requests.Session, progress_prefix: str = "") -> Tuple[bool, int]:
    """
    Download a file from URL and save it to the specified path with progress indicator.
    
    Args:
        url: The file URL to download
        output_path: Path where to save the file
        session: Requests session for connection reuse
        progress_prefix: Prefix for progress messages
    
    Returns:
        Tuple of (success: bool, file_size: int)
    """
    try:
        # Skip empty URLs or anchor links
        if not url or url.startswith('#'):
            return False, 0
        
        # Handle data URIs (embedded content)
        if url.startswith('data:'):
            return False, 0
        
        # Add headers to avoid being blocked
        headers = {
            'Referer': session.headers.get('Referer', ''),
            'Accept': '*/*'
        }
        
        response = session.get(url, timeout=10, stream=True, headers=headers)
        response.raise_for_status()
        
        # Get file size for progress indication
        total_size = int(response.headers.get('content-length', 0))
        
        # Determine file extension if not present
        if not output_path.suffix:
            ext = get_file_extension(url, response.headers.get('content-type', ''))
            output_path = output_path.with_suffix(ext)
        
        # Create parent directories if they don't exist
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # Save the file with progress
        downloaded = 0
        with open(output_path, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
                    downloaded += len(chunk)
                    if total_size > 0 and downloaded % (total_size // 10) < 8192:  # Show progress every 10%
                        percent = (downloaded / total_size) * 100
                        logger.log(PROGRESS, f"{progress_prefix}  ⚡ Progress: {percent:.1f}%")
        
        logger.log(SUCCESS, f"{progress_prefix}  ✓ Downloaded: {output_path.name} ({downloaded/1024:.1f} KB)")
        return True, downloaded
        
    except requests.exceptions.RequestException as e:
        logger.error(f"{progress_prefix}  ✗ Failed to download {url}: {e}")
        return False, 0
    except OSError as e:
        logger.error(f"{progress_prefix}  ✗ Failed to save {output_path}: {e}")
        return False, 0


def download_css_with_resources(css_url: str, output_dir: Path, session: requests.Session, base_url: str, depth: int = 0, max_depth: int = 3) -> Tuple[str, Set[str]]:
    """
    Download CSS file and extract its resources.
    
    Args:
        css_url: URL of CSS file
        output_dir: Directory to save CSS
        session: Requests session
        base_url: Base URL for resolving paths
        depth: Current recursion depth
        max_depth: Maximum recursion depth
    
    Returns:
        Tuple of (css_content: str, found_resources: Set[str])
    """
    if depth >= max_depth:
        return "", set()
    
    try:
        response = session.get(css_url, timeout=10)
        response.raise_for_status()
        css_content = response.text
        
        # Save CSS file
        parsed = urlparse(css_url)
        filename = os.path.basename(parsed.path) or 'style.css'
        if not filename.endswith('.css'):
            filename += '.css'
        
        css_path = output_dir / filename
        css_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(css_path, 'w', encoding='utf-8') as f:
            f.write(css_content)
        
        # Extract resources from CSS
        resources = extract_css_resources(css_content, base_url)
        
        # Recursively download imported CSS
        import_pattern = r'@import\s+[\'"]?([^\'";]+)[\'"]?'
        imports = re.findall(import_pattern, css_content)
        
        for imp in imports:
            if imp and not imp.startswith('data:'):
                imp_url = urljoin(base_url, imp.strip())
                if imp_url != css_url:  # Avoid infinite recursion
                    sub_css_content, sub_resources = download_css_with_resources(
                        imp_url, output_dir, session, base_url, depth + 1, max_depth
                    )
                    resources.update(sub_resources)
        
        return css_content, resources
        
    except Exception as e:
        logger.error(f"  ✗ Failed to process CSS {css_url}: {e}")
        return "", set()


def update_html_paths(soup: BeautifulSoup, base_url: str, output_dir: Path, file_mapping: Dict[str, str]) -> None:
    """
    Update HTML paths to point to downloaded local files.
    
    Args:
        soup: BeautifulSoup object of the HTML
        base_url: Base URL for resolving paths
        output_dir: Directory where files are saved
        file_mapping: Mapping of original URLs to local filenames
    """
    # Update image paths
    for img in soup.find_all('img', src=True):
        src = img['src']
        if src and not src.startswith('data:'):
            full_url = urljoin(base_url, src)
            if full_url in file_mapping:
                img['src'] = file_mapping[full_url]
    
    # Update CSS paths
    for link in soup.find_all('link', rel='stylesheet', href=True):
        href = link['href']
        if href:
            full_url = urljoin(base_url, href)
            if full_url in file_mapping:
                link['href'] = file_mapping[full_url]
    
    # Update JavaScript paths
    for script in soup.find_all('script', src=True):
        src = script['src']
        if src:
            full_url = urljoin(base_url, src)
            if full_url in file_mapping:
                script['src'] = file_mapping[full_url]
    
    # Update video sources
    for video in soup.find_all('video', src=True):
        src = video['src']
        if src:
            full_url = urljoin(base_url, src)
            if full_url in file_mapping:
                video['src'] = file_mapping[full_url]
    
    for source in soup.find_all('source', src=True):
        src = source['src']
        if src:
            full_url = urljoin(base_url, src)
            if full_url in file_mapping:
                source['src'] = file_mapping[full_url]


def create_statistics(output_dir: Path, stats: dict) -> None:
    """
    Create a statistics file with clone information.
    
    Args:
        output_dir: Directory where files are saved
        stats: Dictionary with statistics
    """
    stats_path = output_dir / 'clone_stats.txt'
    with open(stats_path, 'w') as f:
        f.write("=" * 60 + "\n")
        f.write("ZEE-CLONE STATISTICS (v1.0.5 Enhanced Edition)\n")
        f.write("=" * 60 + "\n\n")
        f.write(f"URL: {stats['url']}\n")
        f.write(f"Date: {stats['date']}\n")
        f.write(f"Time taken: {stats['time']:.2f} seconds\n")
        f.write(f"Total files downloaded: {stats['files_downloaded']}\n")
        f.write(f"Total size: {stats['total_size']:.2f} MB\n\n")
        
        f.write("BREAKDOWN BY TYPE:\n")
        f.write("-" * 40 + "\n")
        f.write(f"HTML files: {stats.get('html_count', 0)}\n")
        f.write(f"CSS files: {stats.get('css_count', 0)}\n")
        f.write(f"JavaScript files: {stats.get('js_count', 0)}\n")
        f.write(f"Images: {stats.get('image_count', 0)}\n")
        f.write(f"Fonts: {stats.get('font_count', 0)}\n")
        f.write(f"Video files: {stats.get('video_count', 0)}\n")
        f.write(f"Audio files: {stats.get('audio_count', 0)}\n")
        f.write(f"Iframes: {stats.get('iframe_count', 0)}\n")
        f.write(f"Other resources: {stats.get('other_count', 0)}\n")
        f.write("=" * 60 + "\n")


def clone_website(url: str, output_dir: Path, depth: int = 1, aggressive: bool = False) -> bool:
    """
    Main function to clone a website.
    
    Args:
        url: The website URL to clone
        output_dir: Directory where to save the cloned website
        depth: Depth for following links (not fully implemented in this version)
        aggressive: If True, try to download even more resources
    
    Returns:
        True if successful, False otherwise
    """
    start_time = time.time()
    file_mapping = {}  # Map original URLs to local filenames
    css_contents = {}  # Store CSS content for resource extraction
    
    stats = {
        'url': url,
        'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        'files_downloaded': 0,
        'html_count': 1,
        'css_count': 0,
        'js_count': 0,
        'image_count': 0,
        'font_count': 0,
        'video_count': 0,
        'audio_count': 0,
        'iframe_count': 0,
        'other_count': 0,
        'total_size': 0,
        'time': 0
    }
    
    # Create a session for connection reuse
    session = requests.Session()
    session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.5',
        'Accept-Encoding': 'gzip, deflate',
        'DNT': '1',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1'
    })
    
    try:
        logger.log(HEADER, f"\n{ColoredFormatter.bold}🌐 TARGET: {url}{ColoredFormatter.reset}\n")
        logger.log(PROGRESS, "📥 Downloading main HTML...")
        
        # Download main HTML
        response = session.get(url, timeout=30)
        response.raise_for_status()
        
        # Parse HTML
        soup = BeautifulSoup(response.text, 'lxml')
        
        # Save original HTML
        html_path = output_dir / 'index.html'
        html_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(html_path, 'w', encoding='utf-8') as f:
            f.write(str(soup))
        
        # Calculate HTML size
        html_size = len(response.content) / (1024 * 1024)  # Convert to MB
        stats['total_size'] += html_size
        
        logger.log(SUCCESS, f"  ✓ Saved HTML to: {html_path}")
        
        # First, download all CSS files to extract resources
        css_links = []
        for link in soup.find_all('link', rel='stylesheet', href=True):
            href = link['href']
            if href:
                full_url = urljoin(url, href)
                css_links.append(full_url)
        
        if css_links:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}🎨 Processing CSS Files ({len(css_links)}){ColoredFormatter.reset}")
            for i, css_url in enumerate(css_links, 1):
                logger.log(PROGRESS, f"[{i}/{len(css_links)}] Processing: {os.path.basename(urlparse(css_url).path)}")
                css_content, css_resources = download_css_with_resources(
                    css_url, output_dir, session, url
                )
                if css_content:
                    css_contents[css_url] = css_content
                    stats['css_count'] += 1
                    file_mapping[css_url] = os.path.basename(urlparse(css_url).path)
        
        # Get all resources from HTML and CSS
        resources = get_all_resources(soup, url, css_contents)
        
        # Update stats counts
        stats['image_count'] = len(resources['images'])
        stats['js_count'] = len(resources['js'])
        stats['font_count'] = len(resources['fonts'])
        stats['video_count'] = len(resources['video'])
        stats['audio_count'] = len(resources['audio'])
        stats['iframe_count'] = len(resources['iframe'])
        stats['other_count'] = len(resources['other'])
        
        # Download JavaScript files
        if resources['js']:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}📜 Downloading JavaScript Files ({len(resources['js'])}){ColoredFormatter.reset}")
            for i, js_url in enumerate(resources['js'], 1):
                parsed = urlparse(js_url)
                filename = os.path.basename(parsed.path) or 'script.js'
                if not filename.endswith('.js'):
                    filename += '.js'
                output_path = output_dir / filename
                success, size = download_file(js_url, output_path, session, f"[{i}/{len(resources['js'])}]")
                if success:
                    stats['files_downloaded'] += 1
                    stats['total_size'] += size / (1024 * 1024)
                    file_mapping[js_url] = filename
        
        # Download images
        if resources['images']:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}🖼️ Downloading Images ({len(resources['images'])}){ColoredFormatter.reset}")
            for i, img_url in enumerate(resources['images'], 1):
                parsed = urlparse(img_url)
                filename = os.path.basename(parsed.path) or 'image'
                # Handle URLs without extensions
                if not os.path.splitext(filename)[1]:
                    filename += '.jpg'
                output_path = output_dir / filename
                success, size = download_file(img_url, output_path, session, f"[{i}/{len(resources['images'])}]")
                if success:
                    stats['files_downloaded'] += 1
                    stats['total_size'] += size / (1024 * 1024)
                    file_mapping[img_url] = filename
        
        # Download fonts
        if resources['fonts']:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}🔤 Downloading Fonts ({len(resources['fonts'])}){ColoredFormatter.reset}")
            for i, font_url in enumerate(resources['fonts'], 1):
                parsed = urlparse(font_url)
                filename = os.path.basename(parsed.path) or 'font'
                output_path = output_dir / 'fonts' / filename
                success, size = download_file(font_url, output_path, session, f"[{i}/{len(resources['fonts'])}]")
                if success:
                    stats['files_downloaded'] += 1
                    stats['total_size'] += size / (1024 * 1024)
                    file_mapping[font_url] = str(Path('fonts') / filename)
        
        # Download video files
        if resources['video']:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}🎬 Downloading Videos ({len(resources['video'])}){ColoredFormatter.reset}")
            for i, video_url in enumerate(resources['video'], 1):
                parsed = urlparse(video_url)
                filename = os.path.basename(parsed.path) or 'video.mp4'
                output_path = output_dir / 'videos' / filename
                success, size = download_file(video_url, output_path, session, f"[{i}/{len(resources['video'])}]")
                if success:
                    stats['files_downloaded'] += 1
                    stats['total_size'] += size / (1024 * 1024)
                    file_mapping[video_url] = str(Path('videos') / filename)
        
        # Download audio files
        if resources['audio']:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}🎵 Downloading Audio ({len(resources['audio'])}){ColoredFormatter.reset}")
            for i, audio_url in enumerate(resources['audio'], 1):
                parsed = urlparse(audio_url)
                filename = os.path.basename(parsed.path) or 'audio.mp3'
                output_path = output_dir / 'audio' / filename
                success, size = download_file(audio_url, output_path, session, f"[{i}/{len(resources['audio'])}]")
                if success:
                    stats['files_downloaded'] += 1
                    stats['total_size'] += size / (1024 * 1024)
                    file_mapping[audio_url] = str(Path('audio') / filename)
        
        # Download other resources
        if resources['other'] and aggressive:
            logger.log(HEADER, f"\n{ColoredFormatter.bold}📦 Downloading Other Resources ({len(resources['other'])}){ColoredFormatter.reset}")
            for i, other_url in enumerate(resources['other'], 1):
                parsed = urlparse(other_url)
                filename = os.path.basename(parsed.path) or 'resource.bin'
                output_path = output_dir / 'other' / filename
                success, size = download_file(other_url, output_path, session, f"[{i}/{len(resources['other'])}]")
                if success:
                    stats['files_downloaded'] += 1
                    stats['total_size'] += size / (1024 * 1024)
                    file_mapping[other_url] = str(Path('other') / filename)
        
        # Update HTML to use local paths
        update_html_paths(soup, url, output_dir, file_mapping)
        
        # Save updated HTML with local paths
        with open(html_path, 'w', encoding='utf-8') as f:
            f.write(str(soup))
        
        # Calculate statistics
        stats['time'] = time.time() - start_time
        
        # Create statistics file
        create_statistics(output_dir, stats)
        
        # Show success summary
        logger.log(HEADER, f"\n{ColoredFormatter.bold}{ColoredFormatter.green}✨ CLONE COMPLETE! ✨{ColoredFormatter.reset}\n")
        logger.log(SUCCESS, f"📁 Location: {output_dir}")
        logger.log(SUCCESS, f"⏱️  Time: {stats['time']:.2f} seconds")
        logger.log(SUCCESS, f"📊 Files: {stats['files_downloaded'] + 1} total ({stats['files_downloaded']} resources + HTML)")
        logger.log(SUCCESS, f"   • HTML: 1 file")
        logger.log(SUCCESS, f"   • CSS: {stats['css_count']} files")
        logger.log(SUCCESS, f"   • JS: {stats['js_count']} files")
        logger.log(SUCCESS, f"   • Images: {stats['image_count']} files")
        logger.log(SUCCESS, f"   • Fonts: {stats['font_count']} files")
        logger.log(SUCCESS, f"   • Videos: {stats['video_count']} files")
        logger.log(SUCCESS, f"   • Audio: {stats['audio_count']} files")
        if stats['other_count'] > 0:
            logger.log(SUCCESS, f"   • Other: {stats['other_count']} files")
        logger.log(SUCCESS, f"💾 Total Size: {stats['total_size']:.2f} MB")
        logger.log(HEADER, f"\n{ColoredFormatter.bold}📝 Statistics saved to: {output_dir}/clone_stats.txt{ColoredFormatter.reset}\n")
        
        return True
        
    except requests.exceptions.RequestException as e:
        logger.error(f"\n❌ Failed to download website: {e}")
        return False
    except Exception as e:
        logger.error(f"\n❌ Unexpected error: {e}")
        return False
    finally:
        session.close()


def main():
    """Main entry point for the CLI tool."""
    # Show banner
    show_banner()
    
    # Parse arguments
    parser = argparse.ArgumentParser(
        description='Clone websites by downloading HTML, CSS, JS, images, fonts, and more',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        add_help=False  # We'll handle help manually
    )
    
    parser.add_argument(
        'url',
        nargs='?',
        help='URL of the website to clone'
    )
    
    parser.add_argument(
        '-o', '--output',
        help='Output directory (default: domain name with underscores)'
    )
    
    parser.add_argument(
        '-d', '--depth',
        type=int,
        default=1,
        help='Depth for following links (default: 1)'
    )
    
    parser.add_argument(
        '--all',
        action='store_true',
        help='Aggressive mode - download all possible resources'
    )
    
    parser.add_argument(
        '-v', '--verbose',
        action='store_true',
        help='Enable verbose output'
    )
    
    parser.add_argument(
        '-h', '--help',
        action='store_true',
        help='Show this help message'
    )
    
    args = parser.parse_args()
    
    # Show help if requested
    if args.help:
        show_usage()
        sys.exit(0)
    
    # If no URL provided, show banner and usage
    if not args.url:
        show_usage()
        sys.exit(1)
    
    # Set verbose logging if requested
    if args.verbose:
        logger.setLevel(logging.DEBUG)
    
    # Determine output directory
    if args.output:
        output_dir = Path(args.output)
    else:
        folder_name = create_folder_name(args.url)
        output_dir = Path.cwd() / folder_name
    
    # Clone the website
    success = clone_website(args.url, output_dir, args.depth, args.all)
    
    # Exit with appropriate code
    sys.exit(0 if success else 1)


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        logger.error(f"\n{ColoredFormatter.red}⚠️  Operation cancelled by user{ColoredFormatter.reset}")
        sys.exit(1)
    except Exception as e:
        logger.error(f"\n{ColoredFormatter.red}❌ Fatal error: {e}{ColoredFormatter.reset}")
        sys.exit(1)
