#!/usr/bin/env python3
"""
KVS (Kernel Video Sharing) Downloader
Handles video extraction from KVS-based tube sites.

Usage:
  python kvs_download.py extract <url>       # Extract video info as JSON
  python kvs_download.py download <url> <output_dir>  # Download video
"""

import sys
import os
import json
import re
import http.cookiejar
from pathlib import Path
from urllib.parse import urlparse

# Get script directory to find settings
SCRIPT_DIR = Path(__file__).parent
PROJECT_DIR = SCRIPT_DIR.parent
SETTINGS_FILE = PROJECT_DIR / 'data' / 'settings.json'

def output_json(data: dict):
    """Print JSON to stdout and exit"""
    print(json.dumps(data))
    sys.exit(0 if data.get('success') else 1)

def output_error(message: str, error_type: str = 'error'):
    """Print error JSON and exit"""
    output_json({
        'success': False,
        'error': message,
        'error_type': error_type
    })

try:
    from curl_cffi import requests
except ImportError:
    output_error('curl_cffi not installed - run: pip3 install curl_cffi', 'missing_dependency')


def get_settings() -> dict:
    """Read settings from settings.json"""
    try:
        if SETTINGS_FILE.exists():
            with open(SETTINGS_FILE) as f:
                return json.load(f)
    except Exception as e:
        print(f"Warning: Could not read settings: {e}", file=sys.stderr)
    return {}


def load_browser_cookies(domain: str = None) -> 'http.cookiejar.CookieJar':
    """
    Load cookies from browser using browser-cookie3.
    This properly decrypts Chrome cookies on Windows/Linux.
    Returns a CookieJar object.
    """
    try:
        import browser_cookie3
    except ImportError:
        print("Warning: browser-cookie3 not installed", file=sys.stderr)
        return None

    try:
        # Try Chrome first (most common)
        cj = browser_cookie3.chrome(domain_name=domain)
        cookie_count = len([c for c in cj])
        print(f"Loaded {cookie_count} cookies from Chrome for {domain or 'all domains'}", file=sys.stderr)
        return cj
    except Exception as e1:
        print(f"Chrome cookies failed: {e1}", file=sys.stderr)
        try:
            # Fallback to Firefox
            cj = browser_cookie3.firefox(domain_name=domain)
            cookie_count = len([c for c in cj])
            print(f"Loaded {cookie_count} cookies from Firefox for {domain or 'all domains'}", file=sys.stderr)
            return cj
        except Exception as e2:
            print(f"Firefox cookies also failed: {e2}", file=sys.stderr)
            return None


def create_session_with_cookies(url: str) -> requests.Session:
    """
    Create a curl_cffi session with cookies from browser.
    Uses browser-cookie3 for proper cookie decryption.
    """
    session = requests.Session()

    settings = get_settings()
    cookie_browser = settings.get('cookieBrowser', 'none')
    cookies_file = settings.get('cookiesFile', '')

    # Get domain from URL for filtering cookies
    parsed = urlparse(url)
    domain = parsed.netloc.replace('www.', '')

    # Priority 1: Manual cookies file (Netscape format)
    if cookies_file and Path(cookies_file).exists():
        try:
            with open(cookies_file) as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        parts = line.split('\t')
                        if len(parts) >= 7:
                            cookie_domain, _, path, secure, expiry, name, value = parts[:7]
                            session.cookies.set(name, value, domain=cookie_domain, path=path)
            print(f"Loaded cookies from file: {cookies_file}", file=sys.stderr)
        except Exception as e:
            print(f"Warning: Could not load cookies file: {e}", file=sys.stderr)

    # Priority 2: Browser cookies using browser-cookie3 (proper decryption)
    elif cookie_browser in ('chrome', 'auto', 'firefox'):
        cj = load_browser_cookies(domain)
        if cj:
            # Transfer cookies from cookiejar to curl_cffi session
            for cookie in cj:
                try:
                    session.cookies.set(cookie.name, cookie.value, domain=cookie.domain, path=cookie.path)
                except Exception as e:
                    pass  # Skip invalid cookies

    return session


def decode_license_code(license_code: str, video_url_template: str) -> str:
    """
    Decode KVS license code to get actual video URL.
    The license code is used to modify the video URL.
    """
    # KVS uses a simple substitution based on license code
    # Format: function/N/ACTUAL_URL
    if 'function/' in video_url_template:
        parts = video_url_template.split('/')
        if len(parts) >= 3:
            # Skip 'function' and the number, join the rest
            return '/'.join(parts[2:]).rstrip('/')
    return video_url_template


def extract_kvs_info(url: str) -> dict:
    """
    Extract video information from a KVS-based site.
    Returns video URLs, title, thumbnail, etc.
    """
    session = create_session_with_cookies(url)

    try:
        resp = session.get(url, impersonate="chrome131", timeout=30)
        resp.raise_for_status()
    except Exception as e:
        return {'success': False, 'error': f'Failed to fetch page: {e}', 'error_type': 'network'}

    html = resp.text

    # Extract title
    title_match = re.search(r'<title>([^<]+)</title>', html, re.I)
    title = title_match.group(1).strip() if title_match else 'Unknown'
    # Clean up title
    title = re.sub(r'\s*[-|]\s*[^|]+$', '', title).strip()

    # Extract video URLs from flashvars or similar
    # KVS pattern: video_url: 'function/0/https://...'
    video_urls = {}

    # Look for different quality URLs
    patterns = [
        (r'video_url:\s*[\'"]([^\'"]+)[\'"]', 'default'),
        (r'video_url_[0-9]+p:\s*[\'"]([^\'"]+)[\'"]', 'auto'),
        (r'video_alt_url:\s*[\'"]([^\'"]+)[\'"]', 'alt'),
        (r'video_alt_url2:\s*[\'"]([^\'"]+)[\'"]', 'alt2'),
    ]

    for pattern, key in patterns:
        match = re.search(pattern, html)
        if match:
            raw_url = match.group(1)
            video_urls[key] = decode_license_code('', raw_url)

    # Also look for direct get_file URLs with quality info
    get_file_urls = re.findall(r'https://[^"\']+/get_file/[^"\']+\.mp4[^"\']*', html)
    for url in get_file_urls:
        # Extract quality from filename
        quality_match = re.search(r'_(\d+p)\.mp4', url)
        if quality_match:
            video_urls[quality_match.group(1)] = url.rstrip('/')
        elif 'default' not in video_urls:
            video_urls['default'] = url.rstrip('/')

    if not video_urls:
        return {'success': False, 'error': 'No video URLs found', 'error_type': 'extraction_failed'}

    # Get best quality URL (prefer 1080p > 720p > default)
    best_url = None
    for quality in ['1080p', '720p', '480p', '360p', 'default', 'alt', 'alt2']:
        if quality in video_urls:
            best_url = video_urls[quality]
            break

    if not best_url:
        best_url = list(video_urls.values())[0]

    # Extract thumbnail
    thumb_match = re.search(r'preview_url:\s*[\'"]([^\'"]+)[\'"]', html)
    if not thumb_match:
        thumb_match = re.search(r'poster=["\']([^"\']+)["\']', html)
    thumbnail = thumb_match.group(1) if thumb_match else ''

    # Extract duration
    duration_match = re.search(r'duration["\']?\s*[:=]\s*["\']?(\d+)', html)
    duration = int(duration_match.group(1)) if duration_match else 0

    return {
        'success': True,
        'title': title,
        'url': best_url,
        'all_urls': video_urls,
        'thumbnail': thumbnail,
        'duration': duration,
        'page_url': url
    }


def download_video(url: str, output_dir: str) -> dict:
    """
    Download video from KVS-based site.
    The video URL must be extracted fresh as tokens are time-limited.
    Uses browser cookies for session authentication.
    """
    session = create_session_with_cookies(url)

    # First, fetch the page to get fresh URLs and title
    try:
        resp = session.get(url, impersonate="chrome131", timeout=30)
        resp.raise_for_status()
    except Exception as e:
        return {'success': False, 'error': f'Failed to fetch page: {e}', 'error_type': 'network'}

    html = resp.text

    # Extract title
    title_match = re.search(r'<title>([^<]+)</title>', html, re.I)
    title = title_match.group(1).strip() if title_match else 'Unknown'
    title = re.sub(r'\s*[-|]\s*[^|]+$', '', title).strip()

    # Look for direct video URLs - prefer highest quality
    video_urls = {}
    get_file_urls = re.findall(r'https://[^"\']+/get_file/[^"\'\s]+\.mp4[^"\'\s]*', html)

    for u in get_file_urls:
        clean_url = u.rstrip('/')
        quality_match = re.search(r'_(\d+)p\.mp4', clean_url)
        if quality_match:
            video_urls[int(quality_match.group(1))] = clean_url
        elif 'default' not in video_urls:
            video_urls[0] = clean_url

    if not video_urls:
        # Try generic video_url patterns
        patterns = [
            r'video_url:\s*[\'"]function/\d+/([^\'"]+)[\'"]',
            r'video_alt_url:\s*[\'"]function/\d+/([^\'"]+)[\'"]',
        ]
        for pattern in patterns:
            match = re.search(pattern, html)
            if match:
                video_urls[0] = match.group(1).rstrip('/')
                break

    # Parse all explicit quality variants (video_url_1080p, video_url_720p, etc.)
    quality_fields = re.findall(
        r'video_url_(\d{3,4})p\s*:\s*[\'"]function/\d+/(https?://[^\'"]+)[\'"]',
        html,
        re.I
    )
    for q, qurl in quality_fields:
        try:
            video_urls[int(q)] = qurl.rstrip('/')
        except Exception:
            pass

    if not video_urls:
        return {'success': False, 'error': 'No video URLs found on page', 'error_type': 'extraction_failed'}

    # Pick best quality
    best_quality = max(video_urls.keys())
    video_url = video_urls[best_quality]

    # Sanitize filename
    safe_title = re.sub(r'[<>:"/\\|?*]', '_', title)[:200]
    output_path = Path(output_dir) / f"{safe_title}.mp4"
    output_path.parent.mkdir(parents=True, exist_ok=True)

    try:
        # Download with proper referrer - use the page URL as referer
        base_url = '/'.join(url.split('/')[:3])
        headers = {
            'Referer': url,
            'Origin': base_url
        }

        print(f"Downloading: {title}", file=sys.stderr)
        print(f"Quality: {best_quality}p" if best_quality > 0 else "Quality: default", file=sys.stderr)
        print(f"URL: {video_url[:80]}...", file=sys.stderr)
        print(f"Output: {output_path}", file=sys.stderr)

        resp = session.get(video_url, impersonate="chrome131", headers=headers, stream=True, timeout=600)

        # Handle redirects
        if resp.status_code in [301, 302, 303, 307, 308]:
            video_url = resp.headers.get('Location', video_url)
            resp = session.get(video_url, impersonate="chrome131", headers=headers, stream=True, timeout=600)

        resp.raise_for_status()

        total_size = int(resp.headers.get('content-length', 0))
        downloaded = 0

        with open(output_path, 'wb') as f:
            for chunk in resp.iter_content(chunk_size=1024*1024):
                if chunk:
                    f.write(chunk)
                    downloaded += len(chunk)
                    if total_size > 0:
                        percent = (downloaded / total_size) * 100
                        print(f"\rProgress: {percent:.1f}% ({downloaded / 1024 / 1024:.1f} MB)",
                              end='', file=sys.stderr)

        print(file=sys.stderr)  # New line after progress

        return {
            'success': True,
            'title': title,
            'output_path': str(output_path),
            'file_size': os.path.getsize(output_path)
        }

    except Exception as e:
        # Try to clean up partial download
        if output_path.exists():
            output_path.unlink()
        return {'success': False, 'error': f'Download failed: {e}', 'error_type': 'download_failed'}


def main():
    if len(sys.argv) < 3:
        output_error('Usage: kvs_download.py <extract|download> <url> [output_dir]', 'usage')

    command = sys.argv[1].lower()
    url = sys.argv[2]

    if command == 'extract':
        result = extract_kvs_info(url)
        output_json(result)

    elif command == 'download':
        if len(sys.argv) < 4:
            output_error('Usage: kvs_download.py download <url> <output_dir>', 'usage')
        output_dir = sys.argv[3]
        result = download_video(url, output_dir)
        output_json(result)

    else:
        output_error(f'Unknown command: {command}. Use: extract, download', 'usage')


if __name__ == '__main__':
    main()
