import os
import asyncio
import threading
import queue
import json
import uuid
import subprocess
import time
import re
from pyrogram import Client, filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, InputMediaPhoto

# ================= SUAS CONFIGURAÇÕES =================
API_ID = 12345678          
API_HASH = "sua_hash"      
BOT_TOKEN = "seu_token"    

# URL DO SEU SITE (Sem barra no final)
BASE_URL = "https://kimmzin.zapto.org" 

# ================= CAMINHOS =================
VIDEO_DIR = "/var/project/videos/drige"
CUT_DIR = "/var/project/videos/cortes" 
TMP_THUMBS = "/tmp/thumbs_bot"

os.makedirs(TMP_THUMBS, exist_ok=True)
os.makedirs(CUT_DIR, exist_ok=True)

# ================= CACHE DE CAMINHOS =================
path_cache = {}

def shorten_path(full_path):
    short_id = uuid.uuid5(uuid.NAMESPACE_URL, full_path).hex[:8]
    path_cache[short_id] = full_path
    return short_id

def get_path(short_id):
    return path_cache.get(short_id)

# ================= SISTEMA DE FILA =================
class BotConversionQueue:
    def __init__(self):
        self.queue = queue.Queue()
        self.active_jobs = {}
        self.completed_jobs = []
        self.lock = threading.Lock()
        threading.Thread(target=self.worker, daemon=True).start()
    
    def add_job(self, job_data):
        job_id = uuid.uuid4().hex
        job_data['id'] = job_id
        job_data['status'] = 'queued'
        job_data['process_percent'] = 0
        job_data['last_update_time'] = 0
        
        with self.lock:
            self.active_jobs[job_id] = job_data
            
        self.queue.put(job_data)
        return job_id

    def worker(self):
        while True:
            job = self.queue.get()
            if job is None: break
            
            job_id = job['id']
            try:
                with self.lock: self.active_jobs[job_id]['status'] = 'processing'
                
                src_file = job['path']
                safe_name = f"cut_{job_id[:8]}.mp4"
                output_path = os.path.join(CUT_DIR, safe_name)
                thumb_output_path = os.path.join(TMP_THUMBS, f"thumb_{job_id[:8]}.jpg")
                
                cmd = [
                    "ffmpeg", "-y", "-progress", "pipe:1",
                    "-ss", str(job['start']), "-t", str(job['duration']),
                    "-i", src_file,
                    "-c:v", "libx264", "-preset", "ultrafast", "-crf", "23",
                    "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart",
                    output_path
                ]
                
                process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, universal_newlines=True)
                
                for line in process.stdout:
                    if 'out_time_ms' in line:
                        match = re.search(r'out_time_ms=(\d+)', line)
                        if match:
                            time_ms = int(match.group(1))
                            total_ms = job['duration'] * 1000000
                            if total_ms > 0:
                                percent = min(int((time_ms / total_ms) * 100), 99)
                                with self.lock: 
                                    self.active_jobs[job_id]['process_percent'] = percent

                process.wait()
                
                try:
                    subprocess.run([
                        "ffmpeg", "-i", output_path, "-ss", "00:00:00", 
                        "-vframes", "1", "-vf", "scale=320:-1", "-q:v", "5", 
                        "-y", thumb_output_path
                    ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                    job['thumb_path'] = thumb_output_path
                except: job['thumb_path'] = None

                job['output_path'] = output_path
                job['status'] = 'completed'
                job['process_percent'] = 100
                
                with self.lock: 
                    self.completed_jobs.append(job)
                    if job_id in self.active_jobs: del self.active_jobs[job_id]
                    
            except Exception as e:
                print(f"Erro Worker: {e}")
                job['status'] = 'failed'
                job['error'] = str(e)
                with self.lock: 
                    self.completed_jobs.append(job)
                    if job_id in self.active_jobs: del self.active_jobs[job_id]
            finally:
                self.queue.task_done()

conversion_queue = BotConversionQueue()

# ================= BOT PYROGRAM =================
app = Client("bot_session", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)

user_sessions = {}

def time_str(seconds):
    return f"{int(seconds//60):02d}:{int(seconds%60):02d}"

def make_bar(percent):
    done = int(percent / 10)
    return "█" * done + "░" * (10 - done)

def parse_time_input(text):
    try:
        if ':' in text:
            parts = text.split(':')
            return int(parts[0]) * 60 + int(parts[1])
        else: return int(text)
    except: return None

def get_nav_thumb(video_path, time_sec):
    thumb_path = os.path.join(TMP_THUMBS, f"nav_{uuid.uuid4().hex}.jpg")
    try:
        subprocess.run([
            "ffmpeg", "-ss", str(time_sec), "-i", video_path,
            "-frames:v", "1", "-q:v", "5", "-vf", "scale=640:-1", 
            "-y", "-loglevel", "error", thumb_path
        ], check=True)
        return thumb_path
    except: return None

@app.on_message(filters.command("start"))
async def start(client, message):
    await message.reply_text("👋 **Cutter WebLink V2.6**\nEscolha um arquivo:", reply_markup=build_browser_keyboard(VIDEO_DIR))

def build_browser_keyboard(current_path, page=0):
    try: items = sorted(os.listdir(current_path))
    except: return None
    buttons = []
    valid_items = [i for i in items if os.path.isdir(os.path.join(current_path, i)) or i.lower().endswith(('.mp4','.mkv','.avi'))]
    chunk = valid_items[page*8:(page+1)*8]
    for item in chunk:
        full = os.path.join(current_path, item)
        sid = shorten_path(full)
        if os.path.isdir(full): buttons.append([InlineKeyboardButton(f"📁 {item}", callback_data=f"dir|{sid}|0")])
        else: buttons.append([InlineKeyboardButton(f"🎬 {item}", callback_data=f"vid|{sid}")])
    
    path_id = shorten_path(current_path)
    nav = []
    if page > 0: nav.append(InlineKeyboardButton("⬅️", callback_data=f"dir|{path_id}|{page-1}"))
    if len(valid_items) > (page+1)*8: nav.append(InlineKeyboardButton("➡️", callback_data=f"dir|{path_id}|{page+1}"))
    if nav: buttons.append(nav)
    if current_path != VIDEO_DIR:
        pid = shorten_path(os.path.dirname(current_path))
        buttons.append([InlineKeyboardButton("🔙 Voltar", callback_data=f"dir|{pid}|0")])
    return InlineKeyboardMarkup(buttons)

@app.on_callback_query(filters.regex(r"^dir\|"))
async def nav_callback(client, cb):
    _, sid, pg = cb.data.split("|")
    path = get_path(sid)
    if path: await cb.message.edit_reply_markup(build_browser_keyboard(path, int(pg)))

@app.on_callback_query(filters.regex(r"^vid\|"))
async def vid_callback(client, cb):
    _, sid = cb.data.split("|")
    path = get_path(sid)
    if not path: return await cb.answer("Erro.")
    
    uid = cb.from_user.id
    user_sessions[uid] = {'path': path, 'name': os.path.basename(path), 'time': 0, 'duration': 30, 'step': None}
    
    thumb = get_nav_thumb(path, 0)
    await client.send_photo(cb.message.chat.id, thumb, f"🎬 **{os.path.basename(path)}**\n⏱ 00:00", reply_markup=build_player_keyboard(0, 30))
    if thumb: os.remove(thumb)

def build_player_keyboard(curr_time, duration):
    t_str = time_str(curr_time)
    d_str = time_str(duration) if duration >= 60 else f"{duration}s"
    return InlineKeyboardMarkup([
        [
            InlineKeyboardButton("⏪ -1m", callback_data="seek|-60"),
            InlineKeyboardButton("◀ -10s", callback_data="seek|-10"),
            InlineKeyboardButton("▶ +10s", callback_data="seek|10"),
            InlineKeyboardButton("⏩ +1m", callback_data="seek|60"),
        ],
        [
            InlineKeyboardButton(f"✏️ Ir p/ Tempo ({t_str})", callback_data="ask_time"),
            InlineKeyboardButton(f"⏱ Dur: {d_str}", callback_data="ask_dur")
        ],
        [InlineKeyboardButton("✂️ CORTAR AGORA", callback_data="do_cut")]
    ])

@app.on_callback_query(filters.regex(r"^seek\|"))
async def seek_cb(client, cb):
    amt = int(cb.data.split("|")[1])
    uid = cb.from_user.id
    if uid not in user_sessions: return await cb.answer("Sessão expirada")
    
    sess = user_sessions[uid]
    sess['time'] = max(0, sess['time'] + amt)
    
    thumb = get_nav_thumb(sess['path'], sess['time'])
    try:
        await cb.message.edit_media(
            InputMediaPhoto(thumb, f"🎬 **{sess['name']}**\n⏱ {time_str(sess['time'])}"),
            reply_markup=build_player_keyboard(sess['time'], sess['duration'])
        )
    except: pass
    if thumb: os.remove(thumb)

@app.on_callback_query(filters.regex(r"^ask_(time|dur)"))
async def ask_input(client, cb):
    mode = cb.data.split("_")[1]
    uid = cb.from_user.id
    user_sessions[uid]['step'] = mode
    msg = "o **Tempo de Início** (ex: 12:30)" if mode == "time" else "a **Duração** (ex: 45)"
    await cb.message.reply(f"✍️ **Digite {msg}:**")
    await cb.answer()

@app.on_message(filters.text & ~filters.command("start"))
async def handle_text(client, msg):
    uid = msg.from_user.id
    if uid not in user_sessions or not user_sessions[uid]['step']: return
    
    val = parse_time_input(msg.text)
    if val is None: return await msg.reply("❌ Formato inválido.")
    
    sess = user_sessions[uid]
    mode = sess['step']
    sess['step'] = None
    
    if mode == "time":
        sess['time'] = max(0, val)
        thumb = get_nav_thumb(sess['path'], sess['time'])
        await msg.reply_photo(thumb, f"🎬 **{sess['name']}**\n⏱ {time_str(sess['time'])}", reply_markup=build_player_keyboard(sess['time'], sess['duration']))
        if thumb: os.remove(thumb)
    else:
        sess['duration'] = max(1, val)
        await msg.reply(f"✅ Duração definida: {sess['duration']}s", reply_markup=build_player_keyboard(sess['time'], sess['duration']))

@app.on_callback_query(filters.regex(r"^do_cut"))
async def do_cut(client, cb):
    uid = cb.from_user.id
    sess = user_sessions[uid]
    job_data = {
        'path': sess['path'], 'start': sess['time'], 'duration': sess['duration'],
        'chat_id': cb.message.chat.id, 'msg_id': cb.message.id
    }
    jid = conversion_queue.add_job(job_data)
    status_msg = await cb.message.reply(f"⚙️ **Iniciando...**\nJob ID: `{jid[:6]}`")
    with conversion_queue.lock:
        conversion_queue.active_jobs[jid]['status_msg_id'] = status_msg.id

async def upload_progress(current, total, client, message_id, chat_id, last_edit_time):
    now = time.time()
    if now - last_edit_time[0] > 3:
        perc = int(current * 100 / total)
        try:
            await client.edit_message_text(chat_id, message_id, f"🚀 **Enviando Telegram:** {perc}%\n[{make_bar(perc)}]")
            last_edit_time[0] = now
        except: pass

async def queue_monitor():
    print("Monitor Iniciado...")
    while True:
        # Atualiza progresso do corte
        active_list = []
        with conversion_queue.lock: active_list = list(conversion_queue.active_jobs.items())
        
        for jid, job in active_list:
            if job['status'] == 'processing' and 'status_msg_id' in job:
                now = time.time()
                if now - job.get('last_update_time', 0) > 3:
                    perc = job['process_percent']
                    try:
                        await app.edit_message_text(job['chat_id'], job['status_msg_id'], f"⚙️ **Renderizando:** {perc}%\n[{make_bar(perc)}]")
                        with conversion_queue.lock: conversion_queue.active_jobs[jid]['last_update_time'] = now
                    except: pass

        # Envia completados
        completed = []
        with conversion_queue.lock:
            if conversion_queue.completed_jobs:
                completed = conversion_queue.completed_jobs[:]
                conversion_queue.completed_jobs = []
        
        for job in completed:
            chat_id = job['chat_id']
            msg_id = job.get('status_msg_id')
            
            try:
                if job['status'] == 'completed':
                    
                    # === 1. GERA LINK WEB E ENVIA PRIMEIRO ===
                    filename = os.path.basename(job['output_path'])
                    download_url = f"{BASE_URL}/cortes/{filename}"
                    btn_link = InlineKeyboardMarkup([[InlineKeyboardButton("🔗 Baixar / Assistir Online", url=download_url)]])
                    
                    # Deleta msg de status antiga para limpar o chat
                    if msg_id: await app.delete_messages(chat_id, msg_id)
                    
                    # Envia msg do Link
                    status_msg = await app.send_message(
                        chat_id,
                        f"✅ **Renderização Concluída!**\n\n🔗 **Link Rápido:**\n`{download_url}`\n\n🚀 *Iniciando upload para o Telegram...*",
                        reply_markup=btn_link,
                        disable_web_page_preview=True
                    )
                    
                    # === 2. UPLOAD PARA TELEGRAM ===
                    last_time = [0]
                    await app.send_video(
                        chat_id, 
                        job['output_path'],
                        caption=f"🎥 **Arquivo de Vídeo**\n⏱ {time_str(job['start'])} + {job['duration']}s",
                        thumb=job.get('thumb_path'),
                        progress=upload_progress,
                        # Usa o ID da msg do link para mostrar o progresso do upload nela mesma (opcional, ou cria nova)
                        progress_args=(app, status_msg.id, chat_id, last_time)
                    )
                    
                    # Finaliza editando a mensagem de link para dizer que acabou
                    await app.edit_message_text(chat_id, status_msg.id, f"✅ **Concluído!**\n\n🔗 **Link:** `{download_url}`", reply_markup=btn_link, disable_web_page_preview=True)

                    if job.get('thumb_path') and os.path.exists(job['thumb_path']): os.remove(job['thumb_path'])
                    
                else:
                    if msg_id: await app.edit_message_text(chat_id, msg_id, f"❌ Falha: {job.get('error')}")
                    else: await app.send_message(chat_id, f"❌ Falha: {job.get('error')}")
            except Exception as e:
                print(f"Erro envio: {e}")
        
        await asyncio.sleep(1)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.create_task(queue_monitor())
    print("Bot Rodando...")
    app.run()

