from django.shortcuts import render
from django.http import JsonResponse, FileResponse, HttpResponse
import yt_dlp
import os
import threading
import re
import time
import json
from django.conf import settings
from django.views.decorators.http import require_http_methods
from .download_manager import download_manager
from .rate_limiter import rate_limiter

def index(request):
    return render(request, 'downloader/index.html')

def get_client_ip(request):
    """Get client IP address"""
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

@require_http_methods(["POST"])
def get_video_info(request):
    try:
        # Rate limiting for info requests
        client_ip = get_client_ip(request)
        if not rate_limiter.is_allowed(client_ip):
            remaining_time = int(rate_limiter.get_reset_time(client_ip))
            return JsonResponse({
                'error': f'Rate limit exceeded. Try again in {remaining_time} seconds.'
            }, status=429)
        
        url = request.POST.get('url', '').strip()
        
        if not url:
            return JsonResponse({'error': 'URL is required'}, status=400)
        
        if 'youtube.com' not in url and 'youtu.be' not in url:
            return JsonResponse({'error': 'Please enter a valid YouTube URL'}, status=400)
        
        ydl_opts = {
    'quiet': False,
    'no_warnings': False,
    'http_headers': {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    },
    'socket_timeout': 30,
    'youtube_include_dash_manifest': False,  // Disable DASH to get simpler formats
}
         # Check if cookies file exists
        cookies_path = os.path.join(settings.BASE_DIR, 'secrets', 'cookies.txt')
        if os.path.exists(cookies_path):
            ydl_opts['cookiefile'] = cookies_path
        
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            info = ydl.extract_info(url, download=False)
            
            formats_list = []
            seen_formats = set()
            
            for f in info.get('formats', []):
                has_video = f.get('vcodec') != 'none'
                has_audio = f.get('acodec') != 'none'
                height = f.get('height', 0)
                
                format_key = f"{height}_{has_video}_{has_audio}_{f.get('ext')}"
                
                if format_key not in seen_formats:
                    seen_formats.add(format_key)
                    
                    if has_video and height:
                        resolution = f"{height}p"
                    elif has_audio and not has_video:
                        resolution = "Audio Only"
                    else:
                        resolution = "Unknown"
                    
                    # Get filesize - try multiple methods
                    filesize = f.get('filesize')
                    if not filesize:
                        filesize = f.get('filesize_approx')
                    
                    # If still no filesize, estimate from bitrate and duration
                    if not filesize and f.get('tbr') and info.get('duration'):
                        # tbr is in kbps, duration in seconds
                        # filesize = (bitrate * duration) / 8 / 1024
                        filesize = int((f.get('tbr') * info.get('duration') * 1024) / 8)
                    
                    formats_list.append({
                        'format_id': f.get('format_id'),
                        'ext': f.get('ext', 'mp4'),
                        'resolution': resolution,
                        'filesize': f.get('filesize') or f.get('filesize_approx', 0),
                        'note': f.get('format_note', ''),
                        'has_video': has_video,
                        'has_audio': has_audio,
                        'fps': f.get('fps'),
                        'height': height,
                    })
            
            def sort_key(x):
                priority = 0
                if x['has_video'] and x['has_audio']:
                    priority = 3
                elif x['has_video']:
                    priority = 2
                elif x['has_audio']:
                    priority = 1
                return (priority, x['height'])
            
            formats_list.sort(key=sort_key, reverse=True)
            
            video_data = {
                'title': info.get('title', 'Unknown Title'),
                'thumbnail': info.get('thumbnail', ''),
                'duration': info.get('duration', 0),
                'formats': formats_list[:20]
            }
            
            return JsonResponse(video_data)
            
    except Exception as e:
        error_msg = str(e)
        if 'Private video' in error_msg:
            error_msg = 'This video is private and cannot be downloaded'
        elif 'Video unavailable' in error_msg:
            error_msg = 'This video is unavailable or has been removed'
        elif 'age-restricted' in error_msg:
            error_msg = 'This video is age-restricted'
        
        return JsonResponse({'error': error_msg}, status=400)

def download_video_background(download_id, url, format_id, output_path, output_template):
    """Background task for downloading video"""
    try:
        download_manager.update_status(download_id, 'downloading')
        
        def progress_hook(d):
            if d['status'] == 'downloading':
                try:
                    # Method 1: Try to get percent directly
                    if '_percent_str' in d:
                        percent_str = d['_percent_str'].strip().replace('%', '')
                        # Remove ANSI color codes (like \x1b[0;94m)
                        percent_str = re.sub(r'\x1b\[[0-9;]*m', '', percent_str)
                        # Remove any remaining non-numeric characters except decimal point
                        percent_str = re.sub(r'[^\d.]', '', percent_str)
                        
                        if percent_str:
                            percent = float(percent_str)
                            download_manager.update_progress(download_id, percent)
                            print(f"Progress for {download_id}: {percent}%")
                    
                    # Method 2: Calculate from downloaded/total bytes
                    elif 'downloaded_bytes' in d and 'total_bytes' in d:
                        percent = (d['downloaded_bytes'] / d['total_bytes']) * 100
                        download_manager.update_progress(download_id, percent)
                        print(f"Progress for {download_id}: {percent}%")  # Debug
                    
                    # Method 3: Use total_bytes_estimate
                    elif 'downloaded_bytes' in d and 'total_bytes_estimate' in d:
                        percent = (d['downloaded_bytes'] / d['total_bytes_estimate']) * 100
                        download_manager.update_progress(download_id, min(percent, 99))  # Cap at 99%
                        print(f"Progress for {download_id}: {percent}%")  # Debug
                    
                except Exception as e:
                    print(f"Progress calculation error: {e}")
                    pass
                    
            elif d['status'] == 'finished':
                download_manager.update_progress(download_id, 100)
                print(f"Download {download_id} finished!")  # Debug
        
        
        ydl_opts = {
            'format': format_id,
            'outtmpl': output_template,
            'quiet': False,
            'no_warnings': False,
            'progress_hooks': [progress_hook],
            'noprogress' : False,
            'no_color' : True,
            'allow_unplayable_formats': True,
            'youtube_include_dash_manifest': True,
        }
        
        # Check if cookies file exists
        cookies_path = os.path.join(settings.BASE_DIR, 'secrets', 'cookies.txt')
        if os.path.exists(cookies_path):
            ydl_opts['cookiefile'] = cookies_path
        
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            info = ydl.extract_info(url, download=True)
            filename = ydl.prepare_filename(info)
            
            download_manager.update_status(download_id, 'ready')
            download_manager.active_downloads[download_id]['filename'] = filename
            
    except Exception as e:
        download_manager.update_status(download_id, 'failed')
        download_manager.active_downloads[download_id]['error'] = str(e)

@require_http_methods(["POST"])
def download_video(request):
    try:
        # Rate limiting
        client_ip = get_client_ip(request)
        if not rate_limiter.is_allowed(client_ip):
            remaining_time = int(rate_limiter.get_reset_time(client_ip))
            return JsonResponse({
                'error': f'Rate limit exceeded. Try again in {remaining_time} seconds.'
            }, status=429)
        
        url = request.POST.get('url', '').strip()
        format_id = request.POST.get('format_id', '').strip()
        
        if not url or not format_id:
            return JsonResponse({'error': 'URL and format_id required'}, status=400)
        
        # Create download directory
        download_path = os.path.join(settings.BASE_DIR, 'downloads')
        os.makedirs(download_path, exist_ok=True)
        
        # Cleanup old files
        download_manager.cleanup_old_files(download_path, max_age_hours=2)
        
        # Create download ID
        download_id = download_manager.create_download_id()
        
        output_template = os.path.join(download_path, f'{download_id}_%(title)s.%(ext)s')
        
        # Add to download manager
        download_manager.add_download(download_id, None, 'pending')
        
        # Start background download
        thread = threading.Thread(
            target=download_video_background,
            args=(download_id, url, format_id, download_path, output_template)
        )
        thread.daemon = True
        thread.start()
        
        # Return download ID for progress tracking
        return JsonResponse({
            'download_id': download_id,
            'message': 'Download started'
        })
    
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)

@require_http_methods(["GET"])
def download_status(request, download_id):
    """Check download status"""
    try:
        download = download_manager.get_download(download_id)
        
        if not download:
            return JsonResponse({'error': 'Download not found'}, status=404)
        
        return JsonResponse({
            'id': download['id'],
            'status': download['status'],
            'progress': download['progress'],
        })
    
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)

@require_http_methods(["GET"])
def download_file(request, download_id):
    """Serve the downloaded file"""
    try:
        download = download_manager.get_download(download_id)
        
        if not download:
            return JsonResponse({'error': 'Download not found'}, status=404)
        
        if download['status'] != 'ready':
            return JsonResponse({'error': 'Download not ready'}, status=400)
        
        filename = download['filename']
        
        if not os.path.exists(filename):
            return JsonResponse({'error': 'File not found'}, status=404)
        
        # Serve file
        response = FileResponse(
            open(filename, 'rb'),
            as_attachment=True,
            filename=os.path.basename(filename)
        )
        
        # Schedule file deletion after 10 seconds
        def delete_after_delay():
            time.sleep(10)
            if os.path.exists(filename):
                try:
                    os.remove(filename)
                    download_manager.remove_download(download_id)
                except Exception as e:
                    print(f"Error deleting file: {e}")
        
        thread = threading.Thread(target=delete_after_delay)
        thread.daemon = True
        thread.start()
        
        return response
    
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)