#!/usr/bin/env python3
"""
Directory File Downloader - Web UI
Flask application with web interface
"""

from flask import Flask, render_template, request, jsonify, send_file
from flask_cors import CORS
import requests
import hashlib
import csv
import time
import os
import threading
from datetime import datetime
from urllib.parse import urljoin, urlparse
from bs4 import BeautifulSoup
from pathlib import Path
import json

app = Flask(__name__)
CORS(app)

# Global state
downloader_state = {
    'running': False,
    'files_found': [],
    'download_log': [],
    'current_file_index': 0,
    'status': '',
    'error': '',
    'directory_url': '',
    'interval': 30,
    'output_dir': 'downloads',
    'total_files': 0,
    'downloaded_files': 0
}

downloader_thread = None
stop_flag = threading.Event()

def calculate_sha256(file_path):
    """Calculate SHA256 hash of a file"""
    sha256_hash = hashlib.sha256()
    with open(file_path, "rb") as f:
        for byte_block in iter(lambda: f.read(4096), b""):
            sha256_hash.update(byte_block)
    return sha256_hash.hexdigest()

def log_download(filename, sha256, size, url, status='SUCCESS', error_msg=''):
    """Add download to log"""
    log_entry = {
        'timestamp': datetime.now().isoformat(),
        'filename': filename,
        'sha256': sha256,
        'size': size,
        'url': url,
        'status': status,
        'error': error_msg
    }
    downloader_state['download_log'].append(log_entry)

def scan_directory(directory_url):
    """Scan directory URL and extract file links"""
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    
    try:
        response = requests.get(directory_url, headers=headers, timeout=30)
        response.raise_for_status()
        
        soup = BeautifulSoup(response.text, 'html.parser')
        links = soup.find_all('a')
        
        files = []
        for link in links:
            href = link.get('href')
            if href and href != '../' and not href.endswith('/') and not href.startswith('?'):
                file_url = urljoin(directory_url, href)
                filename = os.path.basename(urlparse(file_url).path)
                
                if filename and not filename.startswith('.'):
                    files.append({
                        'url': file_url,
                        'filename': filename
                    })
        
        return files, None
        
    except Exception as e:
        return None, str(e)

def download_file(file_info, output_dir):
    """Download a single file and calculate its SHA256"""
    filename = file_info['filename']
    url = file_info['url']
    file_path = os.path.join(output_dir, filename)
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    
    try:
        downloader_state['status'] = f"Downloading {filename}..."
        
        response = requests.get(url, headers=headers, timeout=60, stream=True)
        response.raise_for_status()
        
        with open(file_path, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if stop_flag.is_set():
                    return False
                f.write(chunk)
        
        file_size = os.path.getsize(file_path)
        
        downloader_state['status'] = f"Calculating SHA256 for {filename}..."
        sha256 = calculate_sha256(file_path)
        
        log_download(filename, sha256, file_size, url, 'SUCCESS')
        downloader_state['status'] = f"Successfully downloaded: {filename}"
        downloader_state['downloaded_files'] += 1
        
        return True
        
    except Exception as e:
        log_download(filename, 'DOWNLOAD_FAILED', 0, url, 'FAILED', str(e))
        downloader_state['error'] = f"Failed to download {filename}: {e}"
        return False

def download_worker():
    """Background worker thread for downloading files"""
    global downloader_state
    
    output_dir = downloader_state['output_dir']
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    
    interval = downloader_state['interval']
    files = downloader_state['files_found']
    
    downloader_state['current_file_index'] = 0
    downloader_state['downloaded_files'] = 0
    
    for i, file_info in enumerate(files):
        if stop_flag.is_set():
            break
        
        downloader_state['current_file_index'] = i
        download_file(file_info, output_dir)
        
        # Wait before next download (except for last file)
        if i < len(files) - 1 and not stop_flag.is_set():
            for _ in range(interval):
                if stop_flag.is_set():
                    break
                time.sleep(1)
    
    downloader_state['running'] = False
    if not stop_flag.is_set():
        downloader_state['status'] = 'All files downloaded!'

@app.route('/')
def index():
    """Serve the main page"""
    return render_template('index.html')

@app.route('/api/scan', methods=['POST'])
def api_scan():
    """Scan directory for files"""
    data = request.json
    directory_url = data.get('directory_url', '')
    
    if not directory_url:
        return jsonify({'error': 'Directory URL is required'}), 400
    
    files, error = scan_directory(directory_url)
    
    if error:
        return jsonify({'error': error}), 500
    
    downloader_state['files_found'] = files
    downloader_state['directory_url'] = directory_url
    downloader_state['total_files'] = len(files)
    downloader_state['status'] = f"Found {len(files)} files"
    
    return jsonify({
        'files': files,
        'count': len(files)
    })

@app.route('/api/start', methods=['POST'])
def api_start():
    """Start downloading files"""
    global downloader_thread, stop_flag
    
    if downloader_state['running']:
        return jsonify({'error': 'Download already in progress'}), 400
    
    if not downloader_state['files_found']:
        return jsonify({'error': 'No files to download. Scan directory first.'}), 400
    
    data = request.json
    downloader_state['interval'] = data.get('interval', 30)
    downloader_state['output_dir'] = data.get('output_dir', 'downloads')
    
    downloader_state['running'] = True
    downloader_state['error'] = ''
    stop_flag.clear()
    
    downloader_thread = threading.Thread(target=download_worker)
    downloader_thread.daemon = True
    downloader_thread.start()
    
    return jsonify({'status': 'Download started'})

@app.route('/api/stop', methods=['POST'])
def api_stop():
    """Stop downloading files"""
    if not downloader_state['running']:
        return jsonify({'error': 'No download in progress'}), 400
    
    stop_flag.set()
    downloader_state['running'] = False
    downloader_state['status'] = 'Stopped by user'
    
    return jsonify({'status': 'Download stopped'})

@app.route('/api/status', methods=['GET'])
def api_status():
    """Get current status"""
    return jsonify({
        'running': downloader_state['running'],
        'status': downloader_state['status'],
        'error': downloader_state['error'],
        'files_found': len(downloader_state['files_found']),
        'current_file_index': downloader_state['current_file_index'],
        'downloaded_files': downloader_state['downloaded_files'],
        'total_files': downloader_state['total_files'],
        'download_log': downloader_state['download_log']
    })

@app.route('/api/export', methods=['GET'])
def api_export():
    """Export download log to CSV"""
    if not downloader_state['download_log']:
        return jsonify({'error': 'No downloads to export'}), 400
    
    csv_path = 'download_log.csv'
    with open(csv_path, 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(['Timestamp', 'Filename', 'SHA256', 'Size (bytes)', 'URL', 'Status'])
        
        for entry in downloader_state['download_log']:
            status_msg = f"ERROR: {entry['error']}" if entry['status'] == 'FAILED' else entry['status']
            writer.writerow([
                entry['timestamp'],
                entry['filename'],
                entry['sha256'],
                entry['size'],
                entry['url'],
                status_msg
            ])
    
    return send_file(csv_path, as_attachment=True, download_name=f"download_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv")

@app.route('/api/reset', methods=['POST'])
def api_reset():
    """Reset all state"""
    if downloader_state['running']:
        return jsonify({'error': 'Cannot reset while download is in progress'}), 400
    
    downloader_state['files_found'] = []
    downloader_state['download_log'] = []
    downloader_state['current_file_index'] = 0
    downloader_state['status'] = ''
    downloader_state['error'] = ''
    downloader_state['total_files'] = 0
    downloader_state['downloaded_files'] = 0
    
    return jsonify({'status': 'Reset complete'})

if __name__ == '__main__':
    print("=" * 70)
    print("Directory File Downloader - Web UI")
    print("=" * 70)
    print("Starting server at http://localhost:5000")
    print("Press Ctrl+C to stop")
    print("=" * 70)
    app.run(debug=True, host='0.0.0.0', port=5000)
