#!/usr/bin/env python3
"""
TikTok Worker - Queue Mode
Stalno provjerava ima li posla i odmah ga izvršava

Pokreni: python app.py
Dashboard: http://localhost:5000
"""

import os
import sys
import json
import time
import threading
from datetime import datetime
from flask import Flask, render_template_string, jsonify, request
from flask_socketio import SocketIO

# ============================================
# KONFIGURACIJA
# ============================================
CONFIG_FILE = 'config.json'

DEFAULT_CONFIG = {
    'server_url': 'http://localhost/ntts/ntts2/dedicated',
    'api_key': '',
    'videos_new': 30,
    'videos_existing': 10,
    'poll_interval': 5,  # Koliko cesto provjerava ima li posla (sekunde)
}

def load_config():
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'r') as f:
            return {**DEFAULT_CONFIG, **json.load(f)}
    return DEFAULT_CONFIG.copy()

def save_config(cfg):
    with open(CONFIG_FILE, 'w') as f:
        json.dump(cfg, f, indent=2)

config = load_config()

# ============================================
# FLASK APP
# ============================================
app = Flask(__name__)
app.config['SECRET_KEY'] = 'tiktok-worker-secret'
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')

# Status
worker_status = {
    'active': False,  # Worker NIJE aktivan dok korisnik ne klikne Start
    'working': False,  # Da li trenutno radi scrape
    'waiting': False,  # Da li ceka na posao
    'current_channel': None,
    'last_check': None,
    'total_synced': 0,
    'total_failed': 0,
    'queue_size': 0
}

worker_thread = None
worker_should_stop = False

log_buffer = []

def log(message, level='info'):
    timestamp = datetime.now().strftime('%H:%M:%S')
    entry = {'time': timestamp, 'message': str(message), 'level': level}
    log_buffer.append(entry)
    if len(log_buffer) > 500:
        log_buffer.pop(0)
    try:
        print(f"[{timestamp}] {message}")
    except:
        pass
    socketio.emit('log', entry)

def broadcast_status():
    socketio.emit('status', worker_status)

# ============================================
# YT-DLP SCRAPER
# ============================================
def scrape_with_ytdlp(username, video_count):
    import yt_dlp
    
    url = f"https://www.tiktok.com/@{username}"
    user_info = {'username': username, 'nickname': username}
    
    # Trazimo vise videa da bi pokrili eventualne greske/pinned videe
    search_limit = video_count + 30
    
    for attempt in range(3):
        videos = []
        
        # Provjeri cookies.txt za precizniji scrape (vidi SVE videe)
        cookie_file = 'cookies.txt' if os.path.exists('cookies.txt') else None
        
        ydl_opts = {
            'quiet': True,
            'no_warnings': True,
            'extract_flat': True, # BITNO: True samo kupi podatke sa liste (brze i manje blokiranja)
            'ignoreerrors': True,
            'playlistend': search_limit,
            'skip_download': True,
            'no_check_certificate': True,
            'socket_timeout': 30,
            'retries': 5,
            'http_headers': {
                 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
                 'Accept-Language': 'en-US,en;q=0.9',
                 'Referer': 'https://www.tiktok.com/',
            }
        }
        
        if cookie_file:
            ydl_opts['cookiefile'] = cookie_file
            if attempt == 0:
                log("  [Auth] Koristim cookies.txt + Flat extraction", 'success')
        
        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                result = ydl.extract_info(url, download=False)
                
                if not result or not result.get('entries'):
                    if attempt < 2:
                        time.sleep(3)
                        continue
                    return {'success': False, 'user': user_info, 'videos': []}
                
                user_info = {
                    'username': username,
                    'nickname': result.get('uploader', username),
                    'avatar': result.get('thumbnail', ''),
                    'followers': result.get('channel_follower_count', 0),
                    'video_count': result.get('playlist_count', 0)
                }
                
                entries = result.get('entries', [])
                valid_entries = 0
                
                for v in entries:
                    if len(videos) >= video_count:
                        break
                        
                    if not v:
                        continue
                    try:
                        desc = v.get('description', '') or ''
                        title = v.get('title', '') or desc[:200] or 'No Title'
                        
                        # Parsiraj datum
                        posted_at = v.get('timestamp', 0)
                        if not posted_at and v.get('upload_date'):
                            try:
                                posted_at = datetime.strptime(v.get('upload_date'), '%Y%m%d').timestamp()
                            except:
                                posted_at = 0
                        
                        videos.append({
                            'tiktok_id': str(v.get('id', '')),
                            'title': title,
                            'views': v.get('view_count', 0) or 0,
                            'likes': v.get('like_count', 0) or 0,
                            'comments': v.get('comment_count', 0) or 0,
                            'shares': v.get('repost_count', 0) or 0,
                            'posted_at': posted_at or 0,
                            'url': v.get('webpage_url', ''),
                            'thumbnail': v.get('thumbnail', ''),
                            'duration': v.get('duration', 0) or 0
                        })
                        valid_entries += 1
                    except:
                        continue
                
                if videos:
                    # Log datum najnovijeg videa za provjeru
                    try:
                        latest = max(videos, key=lambda x: x['posted_at'])
                        date_str = datetime.fromtimestamp(latest['posted_at']).strftime('%d.%m.%Y %H:%M') if latest['posted_at'] > 0 else "NEMA DATUMA"
                        log(f"  Najnoviji video: {date_str}", 'success')
                    except:
                        pass
                        
                    return {'success': True, 'user': user_info, 'videos': videos}
                    
        except Exception as e:
            if attempt < 2:
                log(f"  Retry {attempt+2}/3 zbog greske: {str(e)[:50]}...", 'warning')
                time.sleep(3)
                continue
    
    return {'success': False, 'user': user_info, 'videos': []}

# ============================================
# API CALLS
# ============================================
def api_call(endpoint, method='GET', data=None):
    import requests
    
    url = f"{config['server_url']}/api/remote-sync.php?action={endpoint}"
    headers = {'X-API-Key': config['api_key'], 'Content-Type': 'application/json'}
    
    try:
        log(f"  [API] {method} {endpoint}...", 'info')
        
        if method == 'GET':
            start = time.time()
            r = requests.get(url, headers=headers, timeout=30)
            elapsed = time.time() - start
            log(f"  [API] GET odgovor za {elapsed:.1f}s", 'success')
        else:
            # Detaljno mjerenje vremena za POST
            data_json = json.dumps(data) if data else '{}'
            data_size = len(data_json)
            log(f"  [API] Saljem {data_size} bytes...", 'info')
            
            # Koristimo session za bolji timing
            session = requests.Session()
            
            # Mjerimo vrijeme konekcije
            t1 = time.time()
            
            # Kreiraj request objekt
            req = requests.Request('POST', url, headers=headers, json=data)
            prepared = session.prepare_request(req)
            
            t2 = time.time()
            log(f"  [API] Request pripremljen za {(t2-t1)*1000:.0f}ms", 'info')
            
            # Pošalji i čekaj odgovor
            r = session.send(prepared, timeout=(10, 120))  # (connect_timeout, read_timeout)
            
            t3 = time.time()
            total = t3 - t1
            send_time = t3 - t2
            
            log(f"  [API] Server odgovorio za {send_time:.1f}s (ukupno: {total:.1f}s, status: {r.status_code})", 
                'success' if r.status_code == 200 else 'warning')
            
            # Ispisi odgovor ako ima timing info
            try:
                resp_data = r.json()
                if 'timings' in resp_data:
                    log(f"  [API] Server timings: {resp_data['timings']}", 'info')
                return resp_data
            except:
                return r.json()
        
        return r.json()
        
    except requests.exceptions.ConnectTimeout:
        log(f"  [API] CONNECT TIMEOUT - server nedostupan!", 'error')
        return {'success': False, 'error': 'Connect timeout'}
    except requests.exceptions.ReadTimeout:
        log(f"  [API] READ TIMEOUT - server ne odgovara!", 'error')
        return {'success': False, 'error': 'Read timeout'}
    except Exception as e:
        log(f"  [API] ERROR: {str(e)[:80]}", 'error')
        return {'success': False, 'error': str(e)}

# ============================================
# WORKER QUEUE LOOP
# ============================================
def worker_queue_loop():
    """Kontinuirano provjerava ima li posla"""
    global worker_status, config, worker_should_stop
    
    log("=" * 40)
    log("WORKER POKRENUT - Queue Mode")
    log(f"Polling: svake {config.get('poll_interval', 5)}s")
    log("=" * 40)
    
    worker_should_stop = False
    
    while not worker_should_stop:
        try:
            worker_status['last_check'] = datetime.now().strftime('%H:%M:%S')
            worker_status['waiting'] = True
            worker_status['working'] = False
            broadcast_status()
            
            # Provjeri ima li posla
            jobs = api_call('pending-jobs')
            
            if not jobs.get('success'):
                log(f"API error: {jobs.get('error')}", 'error')
                time.sleep(10)
                continue
            
            channels = jobs.get('jobs', [])
            worker_status['queue_size'] = len(channels)
            
            if not channels:
                # Nema posla - cekaj pa provjeri opet
                time.sleep(config.get('poll_interval', 5))
                continue
            
            # IMA POSLA - odmah radi!
            worker_status['waiting'] = False
            worker_status['working'] = True
            broadcast_status()
            
            log(f"Novi posao: {len(channels)} kanala")
            
            for i, ch in enumerate(channels, 1):
                username = ch['username']
                is_new = int(ch.get('is_new', 0)) == 1
                existing = int(ch.get('existing_videos', 0))
                
                video_count = config['videos_new'] if (is_new or existing == 0) else config['videos_existing']
                
                # Provjeri stop PRIJE obrade svakog kanala
                if worker_should_stop:
                    log("Stop signal primljen, prekidam...", 'warning')
                    break
                
                worker_status['current_channel'] = f"@{username}"
                broadcast_status()
                
                log(f"[{i}/{len(channels)}] @{username}")
                
                # Scrape
                result = scrape_with_ytdlp(username, video_count)
                
                # Provjeri stop NAKON scrape-a
                if worker_should_stop:
                    log("Stop signal primljen, prekidam...", 'warning')
                    break
                
                # Ako je scrape potpuno neuspjesan (nema user info), onda je fail
                # Ali ako nema videa, a ima user info, to je validan prazan kanal
                user_ok = result.get('user', {}).get('nickname')
                
                if not user_ok and not result.get('success'):
                    log(f"  FAIL - nemoguce dohvatiti kanal", 'error')
                    worker_status['total_failed'] += 1
                    continue
                
                if not result.get('videos'):
                    log(f"  Info: Kanal nema videa ili su nedostupni", 'warning')
                else:
                    log(f"  Dohvaceno {len(result['videos'])} videa")
                
                # Provjeri stop PRIJE sync-a
                if worker_should_stop:
                    log("Stop signal primljen, prekidam...", 'warning')
                    break
                
                # Sync
                sync = api_call('sync-channel', 'POST', {
                    'channel_id': ch['id'],
                    'username': username,
                    'user': result.get('user'),
                    'videos': result.get('videos')
                })
                
                if sync.get('success'):
                    new_v = sync.get('new_videos', 0)
                    upd_v = sync.get('updated_videos', 0)
                    log(f"  OK: +{new_v} new, ~{upd_v} updated", 'success')
                    worker_status['total_synced'] += 1
                else:
                    log(f"  SYNC ERROR: {sync.get('error')}", 'error')
                    worker_status['total_failed'] += 1
                
                broadcast_status()
                time.sleep(1)  # Mala pauza izmedju kanala
            
            worker_status['current_channel'] = None
            worker_status['queue_size'] = 0
            if not worker_should_stop:
                log("Posao zavrsen, cekam novi...")
            
        except Exception as e:
            log(f"Error: {e}", 'error')
            time.sleep(5)
    
    # Worker zaustavljen
    worker_status['active'] = False
    worker_status['working'] = False
    worker_status['waiting'] = False
    broadcast_status()
    log("WORKER ZAUSTAVLJEN", 'warning')

# ============================================
# DASHBOARD HTML
# ============================================
DASHBOARD_HTML = '''
<!DOCTYPE html>
<html lang="sr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TikTok Worker</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.6.0/socket.io.min.js"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
        body { background: #0f172a; color: #f1f5f9; font-family: 'Inter', 'Segoe UI', sans-serif; font-size: 15px; }
        .navbar { background: linear-gradient(135deg, #e94560 0%, #c73e54 100%); padding: 18px 0; box-shadow: 0 4px 20px rgba(233,69,96,0.3); }
        .navbar-brand { font-size: 1.4rem; font-weight: 700; color: #fff !important; }
        .card { background: #1e293b; border: 1px solid #334155; border-radius: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
        .card-header { background: #334155; color: #f8fafc; font-weight: 600; padding: 16px 20px; font-size: 1rem; border-radius: 16px 16px 0 0 !important; }
        .card-body { color: #e2e8f0; padding: 24px; }
        .form-control { background: #1e293b; border: 2px solid #475569; color: #f1f5f9; font-size: 14px; padding: 10px 14px; }
        .form-control:focus { background: #1e293b; color: #f1f5f9; border-color: #e94560; box-shadow: 0 0 0 3px rgba(233,69,96,0.2); }
        .form-control::placeholder { color: #94a3b8; }
        .form-label { color: #f1f5f9; font-weight: 600; margin-bottom: 8px; font-size: 0.9rem; }
        .btn-primary { background: #e94560; border-color: #e94560; font-weight: 600; padding: 10px 20px; }
        .btn-primary:hover { background: #d13a54; border-color: #d13a54; }
        .btn-success { background: #10b981; border-color: #10b981; font-weight: 600; }
        .btn-success:hover { background: #059669; }
        .btn-danger { background: #ef4444; border-color: #ef4444; font-weight: 600; }
        .btn-danger:hover { background: #dc2626; }
        .console { 
            background: #0f172a; 
            border: 2px solid #334155; 
            border-radius: 12px; 
            height: 500px; 
            overflow-y: auto; 
            font-family: 'JetBrains Mono', 'Consolas', monospace; 
            font-size: 13px; 
            padding: 16px;
            color: #e2e8f0;
            line-height: 1.6;
        }
        .console-line { margin: 4px 0; padding: 2px 0; }
        .console-line.info { color: #e2e8f0; }
        .console-line.success { color: #4ade80; font-weight: 500; }
        .console-line.error { color: #f87171; font-weight: 500; }
        .console-line.warning { color: #fbbf24; font-weight: 500; }
        .console-line .time { color: #64748b; margin-right: 12px; font-weight: 500; }
        .status-indicator { 
            display: inline-flex; 
            align-items: center; 
            padding: 10px 20px; 
            border-radius: 25px; 
            font-weight: 600; 
            font-size: 0.95rem;
        }
        .status-working { background: rgba(16,185,129,0.25); color: #4ade80; border: 1px solid rgba(74,222,128,0.3); }
        .status-waiting { background: rgba(251,191,36,0.25); color: #fbbf24; border: 1px solid rgba(251,191,36,0.3); }
        .status-stopped { background: rgba(239,68,68,0.25); color: #f87171; border: 1px solid rgba(248,113,113,0.3); }
        .status-dot { 
            width: 12px; 
            height: 12px; 
            border-radius: 50%; 
            margin-right: 10px; 
            animation: pulse 1.5s infinite;
        }
        .status-working .status-dot { background: #4ade80; box-shadow: 0 0 10px #4ade80; }
        .status-waiting .status-dot { background: #fbbf24; box-shadow: 0 0 10px #fbbf24; }
        .status-stopped .status-dot { background: #f87171; animation: none; }
        @keyframes pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(0.9); } }
        .stat-box { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 20px; text-align: center; }
        .stat-value { font-size: 2rem; font-weight: 700; color: #f8fafc; }
        .stat-label { color: #94a3b8; font-size: 0.85rem; font-weight: 500; margin-top: 4px; }
        .current-channel { 
            background: rgba(233,69,96,0.2); 
            color: #fca5a5; 
            padding: 8px 18px; 
            border-radius: 20px;
            font-weight: 600;
            border: 1px solid rgba(233,69,96,0.3);
        }
        .text-success { color: #4ade80 !important; }
        .text-danger { color: #f87171 !important; }
        .text-warning { color: #fbbf24 !important; }
        .text-muted { color: #94a3b8 !important; }
        .control-btn { min-width: 120px; font-size: 1rem; }
    </style>
</head>
<body>
    <nav class="navbar navbar-dark mb-4">
        <div class="container-fluid px-4">
            <span class="navbar-brand"><i class="fab fa-tiktok me-2"></i>TikTok Worker</span>
            <div class="d-flex align-items-center gap-3">
                <span id="currentChannel" class="current-channel" style="display:none;"></span>
                <button id="btnStart" class="btn btn-success control-btn" onclick="startWorker()">
                    <i class="fas fa-play me-2"></i>Start
                </button>
                <button id="btnStop" class="btn btn-danger control-btn" onclick="stopWorker()" style="display:none;">
                    <i class="fas fa-stop me-2"></i>Stop
                </button>
                <span id="statusIndicator" class="status-indicator status-stopped">
                    <span class="status-dot"></span>
                    <span id="statusText">Stopped</span>
                </span>
            </div>
        </div>
    </nav>
    
    <div class="container-fluid px-4">
        <div class="row mb-3">
            <div class="col-md-2">
                <div class="stat-box">
                    <div class="stat-value text-success" id="totalSynced">0</div>
                    <div class="stat-label">Synced</div>
                </div>
            </div>
            <div class="col-md-2">
                <div class="stat-box">
                    <div class="stat-value text-danger" id="totalFailed">0</div>
                    <div class="stat-label">Failed</div>
                </div>
            </div>
            <div class="col-md-2">
                <div class="stat-box">
                    <div class="stat-value text-warning" id="queueSize">0</div>
                    <div class="stat-label">Queue</div>
                </div>
            </div>
            <div class="col-md-2">
                <div class="stat-box">
                    <div class="stat-value" id="lastCheck">-</div>
                    <div class="stat-label">Last Check</div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="stat-box">
                    <div class="text-center text-muted">
                        <i class="fas fa-info-circle me-1"></i>
                        Worker automatski procesira sve zahtjeve sa servera
                    </div>
                </div>
            </div>
        </div>
        
        <div class="row">
            <div class="col-md-9">
                <div class="card">
                    <div class="card-header d-flex justify-content-between">
                        <span><i class="fas fa-terminal me-2"></i>Live Console</span>
                        <button class="btn btn-sm btn-outline-light" onclick="clearConsole()">Clear</button>
                    </div>
                    <div class="card-body p-0">
                        <div class="console" id="console"></div>
                    </div>
                </div>
            </div>
            
            <div class="col-md-3">
                <div class="card">
                    <div class="card-header"><i class="fas fa-cog me-2"></i>Config</div>
                    <div class="card-body">
                        <div class="mb-3">
                            <label class="form-label">Server URL</label>
                            <input type="text" class="form-control form-control-sm" id="serverUrl" value="{{ config.server_url }}">
                        </div>
                        <div class="mb-3">
                            <label class="form-label">API Key</label>
                            <input type="text" class="form-control form-control-sm" id="apiKey" value="{{ config.api_key }}">
                        </div>
                        <div class="row mb-3">
                            <div class="col-6">
                                <label class="form-label">New vids</label>
                                <input type="number" class="form-control form-control-sm" id="videosNew" value="{{ config.videos_new }}">
                            </div>
                            <div class="col-6">
                                <label class="form-label">Existing</label>
                                <input type="number" class="form-control form-control-sm" id="videosExisting" value="{{ config.videos_existing }}">
                            </div>
                        </div>
                        <div class="mb-3 p-2" style="background: rgba(0,0,0,0.2); border-radius: 6px;">
                            <label class="form-label d-flex justify-content-between">
                                <span>Auth Mode:</span>
                                <span id="authStatus" class="badge bg-secondary">Guest</span>
                            </label>
                            <div class="input-group input-group-sm mt-2">
                                <label class="btn btn-outline-light btn-sm" for="cookieFile">
                                    <i class="fas fa-file-upload"></i> Upload cookies.txt
                                </label>
                                <input type="file" class="form-control d-none" id="cookieFile" accept=".txt">
                                <button class="btn btn-outline-danger btn-sm" onclick="deleteCookies()" title="Delete Cookies">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </div>
                            <small class="text-muted" style="font-size: 0.75em; display: block; margin-top: 4px;">
                                Uploaduj 'cookies.txt' za pristup 18+/pinned videima.
                            </small>
                        </div>

                        <div class="mb-3">
                            <label class="form-label">Poll interval (sec)</label>
                            <input type="number" class="form-control form-control-sm" id="pollInterval" value="{{ config.poll_interval }}" min="1" max="60">
                            <small class="text-muted">Koliko cesto provjerava server</small>
                        </div>
                        <button class="btn btn-primary btn-sm w-100" onclick="saveConfig()">
                            <i class="fas fa-save me-1"></i>Save
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <script>
        const socket = io();
        const consoleEl = document.getElementById('console');
        
        // Cookie upload handler
        document.getElementById('cookieFile').addEventListener('change', function(e) {
            if (!this.files[0]) return;
            
            const formData = new FormData();
            formData.append('file', this.files[0]);
            
            fetch('/api/upload-cookies', {method: 'POST', body: formData})
                .then(r => r.json())
                .then(d => {
                    if(d.success) alert('Cookies uploadovani!'); 
                    else alert('Error: ' + d.error);
                });
            this.value = ''; // reset
        });

        function deleteCookies() {
            if(confirm('Obrisati cookies.txt?')) {
                fetch('/api/delete-cookies', {method: 'POST'});
            }
        }
        
        socket.on('log', function(data) {
            const line = document.createElement('div');
            line.className = 'console-line ' + data.level;
            line.innerHTML = '<span class="time">[' + data.time + ']</span>' + escapeHtml(data.message);
            consoleEl.appendChild(line);
            consoleEl.scrollTop = consoleEl.scrollHeight;
        });
        
        socket.on('status', updateUI);
        
        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
        
        function updateUI(data) {
            const indicator = document.getElementById('statusIndicator');
            const statusText = document.getElementById('statusText');
            const currentCh = document.getElementById('currentChannel');
            const authStatus = document.getElementById('authStatus');
            const btnStart = document.getElementById('btnStart');
            const btnStop = document.getElementById('btnStop');
            
            // Handle Start/Stop button visibility
            if (data.active) {
                btnStart.style.display = 'none';
                btnStop.style.display = 'inline-block';
                
                if (data.working) {
                    indicator.className = 'status-indicator status-working';
                    statusText.textContent = 'Working';
                } else {
                    indicator.className = 'status-indicator status-waiting';
                    statusText.textContent = 'Waiting';
                }
            } else {
                btnStart.style.display = 'inline-block';
                btnStop.style.display = 'none';
                indicator.className = 'status-indicator status-stopped';
                statusText.textContent = 'Stopped';
            }
            
            if (data.current_channel) {
                currentCh.textContent = data.current_channel;
                currentCh.style.display = 'inline';
            } else {
                currentCh.style.display = 'none';
            }
            
            if (data.cookies_present) {
                authStatus.className = 'badge bg-success';
                authStatus.innerHTML = '<i class="fas fa-lock me-1"></i>Auth';
            } else {
                authStatus.className = 'badge bg-secondary';
                authStatus.innerHTML = '<i class="fas fa-user-secret me-1"></i>Guest';
            }
            
            document.getElementById('totalSynced').textContent = data.total_synced || 0;
            document.getElementById('totalFailed').textContent = data.total_failed || 0;
            document.getElementById('queueSize').textContent = data.queue_size || 0;
            document.getElementById('lastCheck').textContent = data.last_check || '-';
        }
        
        function startWorker() {
            fetch('/api/start', {method: 'POST'})
                .then(r => r.json())
                .then(d => {
                    if (!d.success) alert(d.error);
                });
        }
        
        function stopWorker() {
            if (confirm('Da li ste sigurni da zelite zaustaviti worker?')) {
                fetch('/api/stop', {method: 'POST'})
                    .then(r => r.json())
                    .then(d => {
                        if (!d.success) alert(d.error);
                    });
            }
        }
        
        function saveConfig() {
            fetch('/api/config', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    server_url: document.getElementById('serverUrl').value,
                    api_key: document.getElementById('apiKey').value,
                    videos_new: parseInt(document.getElementById('videosNew').value),
                    videos_existing: parseInt(document.getElementById('videosExisting').value),
                    poll_interval: parseInt(document.getElementById('pollInterval').value)
                })
            }).then(() => alert('Saved!'));
        }
        
        function clearConsole() { consoleEl.innerHTML = ''; }
        
        // Polling backup
        setInterval(() => fetch('/api/status').then(r => r.json()).then(updateUI), 2000);
        
        // Initial
        fetch('/api/status').then(r => r.json()).then(updateUI);
        fetch('/api/logs').then(r => r.json()).then(logs => {
            logs.forEach(l => {
                const line = document.createElement('div');
                line.className = 'console-line ' + l.level;
                line.innerHTML = '<span class="time">[' + l.time + ']</span>' + escapeHtml(l.message);
                consoleEl.appendChild(line);
            });
            consoleEl.scrollTop = consoleEl.scrollHeight;
        });
    </script>
</body>
</html>
'''

@app.route('/')
def dashboard():
    return render_template_string(DASHBOARD_HTML, config=config)

@app.route('/api/status')
def get_status():
    return jsonify(worker_status)

@app.route('/api/logs')
def get_logs():
    return jsonify(log_buffer[-200:])

@app.route('/api/config', methods=['GET', 'POST'])
def handle_config():
    global config
    if request.method == 'POST':
        data = request.get_json()
        config.update(data)
        save_config(config)
        return jsonify({'success': True})
    return jsonify(config)

@app.route('/api/upload-cookies', methods=['POST'])
def upload_cookies():
    try:
        if 'file' not in request.files:
            return jsonify({'success': False, 'error': 'Nema fajla'})
        file = request.files['file']
        if file.filename == '':
            return jsonify({'success': False, 'error': 'Nije odabran fajl'})
        
        # Sacuvaj kao cookies.txt
        file.save('cookies.txt')
        log("Novi cookies.txt USPJESNO uploadovan!", 'success')
        
        # Update status odma
        worker_status['cookies_present'] = True
        broadcast_status()
        
        return jsonify({'success': True, 'message': 'Cookies sacuvani!'})
    except Exception as e:
        log(f"Upload greska: {e}", 'error')
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/delete-cookies', methods=['POST'])
def delete_cookies():
    try:
        if os.path.exists('cookies.txt'):
            os.remove('cookies.txt')
            worker_status['cookies_present'] = False
            broadcast_status()
            log("cookies.txt obrisan", 'warning')
        return jsonify({'success': True})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/start', methods=['POST'])
def start_worker():
    global worker_thread, worker_should_stop
    
    if worker_status['active']:
        return jsonify({'success': False, 'error': 'Worker vec radi!'})
    
    worker_should_stop = False
    worker_status['active'] = True
    worker_thread = threading.Thread(target=worker_queue_loop, daemon=True)
    worker_thread.start()
    broadcast_status()
    
    return jsonify({'success': True, 'message': 'Worker pokrenut!'})

@app.route('/api/stop', methods=['POST'])
def stop_worker():
    global worker_should_stop
    
    if not worker_status['active']:
        return jsonify({'success': False, 'error': 'Worker vec ne radi!'})
    
    worker_should_stop = True
    log("Zaustavljam worker...", 'warning')
    
    return jsonify({'success': True, 'message': 'Worker se zaustavlja...'})

# ============================================
# DIRECT SCRAPE API (za PHP server)
# ============================================
@app.route('/api/scrape', methods=['POST'])
def scrape_direct():
    """
    Direktan scrape jednog kanala - poziva ga PHP server.
    Prima: {username: string, channel_id?: int}
    Vraća: {success, user, videos}
    """
    try:
        data = request.get_json() or {}
        username = data.get('username', '').strip().lstrip('@')
        channel_id = data.get('channel_id')
        
        if not username:
            return jsonify({'success': False, 'error': 'Username obavezan'})
        
        log(f"[SCRAPE] Direktan zahtjev za @{username}", 'info')
        
        # Odredi broj videa za dohvat
        video_count = config.get('videos_existing', 20)
        
        # Scrape odmah
        result = scrape_with_ytdlp(username, video_count)
        
        if result['success']:
            log(f"[SCRAPE] @{username}: {len(result.get('videos', []))} videa", 'success')
        else:
            log(f"[SCRAPE] @{username}: Greška - {result.get('error', 'nepoznato')}", 'error')
        
        # Dodaj channel_id u odgovor ako je poslan
        if channel_id:
            result['channel_id'] = channel_id
            
        return jsonify(result)
        
    except Exception as e:
        log(f"[SCRAPE] Greška: {str(e)}", 'error')
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/scrape-batch', methods=['POST'])
def scrape_batch():
    """
    Scrape više kanala odjednom.
    Prima: {channels: [{username, channel_id}, ...]}
    Vraća: {success, results: [...]}
    """
    try:
        data = request.get_json() or {}
        channels = data.get('channels', [])
        
        if not channels:
            return jsonify({'success': False, 'error': 'Nema kanala za scrape'})
        
        log(f"[BATCH] Scrape {len(channels)} kanala", 'info')
        
        results = []
        video_count = config.get('videos_existing', 20)
        
        for ch in channels:
            username = ch.get('username', '').strip().lstrip('@')
            channel_id = ch.get('channel_id')
            
            if not username:
                continue
                
            log(f"[BATCH] Scraping @{username}...", 'info')
            result = scrape_with_ytdlp(username, video_count)
            result['channel_id'] = channel_id
            result['username'] = username
            results.append(result)
            
            if result['success']:
                log(f"[BATCH] @{username}: OK ({len(result.get('videos', []))} videa)", 'success')
            else:
                log(f"[BATCH] @{username}: Greška", 'warning')
        
        log(f"[BATCH] Završeno - {len(results)} kanala", 'success')
        
        return jsonify({
            'success': True,
            'count': len(results),
            'results': results
        })
        
    except Exception as e:
        log(f"[BATCH] Greška: {str(e)}", 'error')
        return jsonify({'success': False, 'error': str(e)})

# ============================================
# MAIN
# ============================================
if __name__ == '__main__':
    # Initial verification
    if os.path.exists('cookies.txt'):
        worker_status['cookies_present'] = True
        print("[INFO] cookies.txt pronadjen - Auth mode aktivan")
    else:
        worker_status['cookies_present'] = False
        print("[INFO] cookies.txt nije pronadjen - Guest mode")

    print("=" * 40)
    print("TikTok Worker - Queue Mode")
    print("=" * 40)
    print(f"Server: {config['server_url']}")
    print("Dashboard: http://0.0.0.0:5000")
    print("Worker ceka na START komandu iz Web UI-a")
    print("=" * 40)
    
    # NE POKRECI AUTOMATSKI - cekaj na korisnika
    socketio.run(app, host='0.0.0.0', port=5000, debug=False, allow_unsafe_werkzeug=True)
