# main.py
import os
import json
import logging
import subprocess
import threading
import time
from datetime import datetime, timedelta
from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
import sqlite3
from werkzeug.utils import secure_filename
import shutil

app = Flask(__name__)
app.config.from_pyfile('config.py')

# Initialize login manager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# Simple user class for authentication
class User(UserMixin):
    def __init__(self, id):
        self.id = id

@login_manager.user_loader
def load_user(user_id):
    return User(user_id)

# Initialize database
def init_db():
    conn = sqlite3.connect('database/iptv.db')
    c = conn.cursor()
    
    # Media table
    c.execute('''
        CREATE TABLE IF NOT EXISTS media (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            filename TEXT UNIQUE,
            original_name TEXT,
            title TEXT,
            description TEXT,
            duration INTEGER,
            file_size INTEGER,
            upload_date DATETIME DEFAULT CURRENT_TIMESTAMP,
            category TEXT,
            tags TEXT,
            is_active BOOLEAN DEFAULT 1
        )
    ''')
    
    # Streams table
    c.execute('''
        CREATE TABLE IF NOT EXISTS streams (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT,
            input_source TEXT,
            output_url TEXT,
            status TEXT DEFAULT 'stopped',
            pid INTEGER,
            start_time DATETIME,
            config TEXT
        )
    ''')
    
    # Schedules table
    c.execute('''
        CREATE TABLE IF NOT EXISTS schedules (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            media_id INTEGER,
            stream_id INTEGER,
            start_time DATETIME,
            end_time DATETIME,
            repeat_type TEXT,
            is_active BOOLEAN DEFAULT 1,
            created_date DATETIME DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (media_id) REFERENCES media (id),
            FOREIGN KEY (stream_id) REFERENCES streams (id)
        )
    ''')
    
    # Check if streams table is empty and insert default streams
    c.execute('SELECT COUNT(*) FROM streams')
    stream_count = c.fetchone()[0]
    
    if stream_count == 0:
        # Create default stream with CORRECT configuration
        c.execute('''
            INSERT INTO streams (name, input_source, output_url, config)
            VALUES (?, ?, ?, ?)
        ''', (
            'Main Stream', 
            'uploads/default.mp4',
            'http://khalil1342.ddns.net:34401/streams/output.m3u8',
            '{"video_codec": "libx264", "audio_codec": "aac", "bitrate": "2000k"}'
        ))
        
        # Create backup stream
        c.execute('''
            INSERT INTO streams (name, input_source, output_url, config)
            VALUES (?, ?, ?, ?)
        ''', (
            'Backup Stream', 
            'uploads/backup.mp4',
            'http://khalil1342.ddns.net:34401/streams/backup.m3u8',
            '{"video_codec": "libx264", "audio_codec": "aac", "bitrate": "1500k"}'
        ))
        print("✅ Default streams added to database")
    
    conn.commit()
    conn.close()

# Ensure directories exist
def create_directories():
    directories = ['uploads', 'database', 'streams', 'logs', 'static/css', 'static/js', 'static/images', 'templates']
    for directory in directories:
        os.makedirs(directory, exist_ok=True)

# Stream manager class
class StreamManager:
    def __init__(self):
        self.active_streams = {}
        self.stream_logs = {}
    
    def create_playlist_from_media(self, media_id):
        """Create an M3U8 playlist from media file for streaming"""
        try:
            conn = sqlite3.connect('database/iptv.db')
            c = conn.cursor()
            c.execute('SELECT filename, title FROM media WHERE id = ?', (media_id,))
            media = c.fetchone()
            conn.close()
            
            if not media:
                return None, "Media not found"
            
            filename = media[0]
            filepath = os.path.join('uploads', filename)
            
            if not os.path.exists(filepath):
                return None, "Media file not found"
            
            # Create streams directory if it doesn't exist
            os.makedirs('streams', exist_ok=True)
            
            # Create a simple M3U8 playlist pointing to the media file
            playlist_path = os.path.join('streams', f'playlist_{media_id}.m3u8')
            
            with open(playlist_path, 'w') as f:
                f.write(f"""#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:10.0,
{filepath}
#EXT-X-ENDLIST
""")
            
            return playlist_path, "Playlist created successfully"
            
        except Exception as e:
            return None, f"Error creating playlist: {str(e)}"
    
    def start_stream(self, stream_id, media_id=None, config=None):
        try:
            if stream_id in self.active_streams:
                self.stop_stream(stream_id)
            
            # If media_id is provided, create playlist from that media
            input_source = ""
            if media_id:
                playlist_path, message = self.create_playlist_from_media(media_id)
                if not playlist_path:
                    return False, message
                input_source = playlist_path
            else:
                # Use default input source
                input_source = 'input.m3u8'
            
            # Output path - this should be where your web server serves the files
            output_dir = 'streams'
            output_pattern = os.path.join(output_dir, 'output_%03d.ts')
            output_playlist = os.path.join(output_dir, 'output.m3u8')
            
            # Ensure output directory exists
            os.makedirs(output_dir, exist_ok=True)
            
            # FFmpeg command for HLS streaming
            ffmpeg_cmd = [
                'ffmpeg',
                '-re',  # Read input at native frame rate
                '-i', input_source,
                '-c:v', 'libx264',
                '-preset', 'veryfast',
                '-maxrate', '2000k',
                '-bufsize', '4000k',
                '-pix_fmt', 'yuv420p',
                '-c:a', 'aac',
                '-b:a', '128k',
                '-f', 'hls',
                '-hls_time', '4',
                '-hls_list_size', '6',
                '-hls_wrap', '10',  # Number of segments to keep
                '-hls_segment_filename', output_pattern,
                '-hls_flags', 'delete_segments',  # Delete old segments
                output_playlist
            ]
            
            # Start FFmpeg process
            process = subprocess.Popen(
                ffmpeg_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                cwd=os.getcwd()  # Set working directory
            )
            
            self.active_streams[stream_id] = {
                'process': process,
                'start_time': datetime.now(),
                'config': config or {},
                'input_source': input_source,
                'media_id': media_id
            }
            
            # Update database
            conn = sqlite3.connect('database/iptv.db')
            c = conn.cursor()
            c.execute('UPDATE streams SET status=?, pid=?, start_time=? WHERE id=?',
                     ('running', process.pid, datetime.now(), stream_id))
            conn.commit()
            conn.close()
            
            # Start log monitoring thread
            threading.Thread(target=self._monitor_stream, args=(stream_id, process), daemon=True).start()
            
            return True, f"Stream started successfully with media ID: {media_id}"
            
        except Exception as e:
            return False, f"Error starting stream: {str(e)}"
    
    def start_stream_with_file(self, stream_id, file_path, config=None):
        """Start stream with direct file path"""
        try:
            if stream_id in self.active_streams:
                self.stop_stream(stream_id)
            
            if not os.path.exists(file_path):
                return False, "File not found"
            
            output_dir = 'streams'
            output_pattern = os.path.join(output_dir, 'output_%03d.ts')
            output_playlist = os.path.join(output_dir, 'output.m3u8')
            
            os.makedirs(output_dir, exist_ok=True)
            
            # FFmpeg command for direct file streaming
            ffmpeg_cmd = [
                'ffmpeg',
                '-re',
                '-i', file_path,
                '-c:v', 'libx264',
                '-preset', 'veryfast',
                '-maxrate', '2000k',
                '-bufsize', '4000k',
                '-pix_fmt', 'yuv420p',
                '-c:a', 'aac',
                '-b:a', '128k',
                '-f', 'hls',
                '-hls_time', '4',
                '-hls_list_size', '6',
                '-hls_wrap', '10',
                '-hls_segment_filename', output_pattern,
                '-hls_flags', 'delete_segments',
                output_playlist
            ]
            
            process = subprocess.Popen(
                ffmpeg_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                cwd=os.getcwd()
            )
            
            self.active_streams[stream_id] = {
                'process': process,
                'start_time': datetime.now(),
                'config': config or {},
                'input_source': file_path,
                'media_id': None
            }
            
            conn = sqlite3.connect('database/iptv.db')
            c = conn.cursor()
            c.execute('UPDATE streams SET status=?, pid=?, start_time=? WHERE id=?',
                     ('running', process.pid, datetime.now(), stream_id))
            conn.commit()
            conn.close()
            
            threading.Thread(target=self._monitor_stream, args=(stream_id, process), daemon=True).start()
            
            return True, "Stream started successfully with direct file"
            
        except Exception as e:
            return False, f"Error starting stream: {str(e)}"

    def stop_stream(self, stream_id):
        try:
            if stream_id in self.active_streams:
                process = self.active_streams[stream_id]['process']
                process.terminate()
                process.wait(timeout=10)
                del self.active_streams[stream_id]
            
            # Update database
            conn = sqlite3.connect('database/iptv.db')
            c = conn.cursor()
            c.execute('UPDATE streams SET status=?, pid=NULL WHERE id=?',
                     ('stopped', stream_id))
            conn.commit()
            conn.close()
            
            return True, "Stream stopped successfully"
        except Exception as e:
            return False, f"Error stopping stream: {str(e)}"
    
    def _monitor_stream(self, stream_id, process):
        while process.poll() is None:
            try:
                line = process.stderr.readline()
                if line:
                    if stream_id not in self.stream_logs:
                        self.stream_logs[stream_id] = []
                    self.stream_logs[stream_id].append(line.strip())
                    # Keep only last 100 lines
                    if len(self.stream_logs[stream_id]) > 100:
                        self.stream_logs[stream_id] = self.stream_logs[stream_id][-100:]
            except:
                break
    
    def get_stream_status(self, stream_id):
        if stream_id in self.active_streams:
            process = self.active_streams[stream_id]['process']
            current_media = self.active_streams[stream_id].get('input_source', 'Unknown')
            return {
                'status': 'running' if process.poll() is None else 'stopped',
                'uptime': str(datetime.now() - self.active_streams[stream_id]['start_time']),
                'logs': self.stream_logs.get(stream_id, [])[-10:],  # Last 10 lines
                'current_media': os.path.basename(current_media)
            }
        return {'status': 'stopped', 'uptime': '0', 'logs': [], 'current_media': 'None'}

# Initialize stream manager
stream_manager = StreamManager()

# Routes
@app.route('/')
@login_required
def index():
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        
        # Simple authentication - replace with proper auth in production
        if username == app.config['ADMIN_USERNAME'] and password == app.config['ADMIN_PASSWORD']:
            user = User(1)
            login_user(user)
            return redirect(url_for('index'))
        else:
            return render_template('login.html', error='Invalid credentials')
    
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

# Media Management Routes
@app.route('/media')
@login_required
def media_management():
    conn = sqlite3.connect('database/iptv.db')
    c = conn.cursor()
    c.execute('SELECT * FROM media ORDER BY upload_date DESC')
    media_files = c.fetchall()
    conn.close()
    
    return render_template('media.html', media_files=media_files)

@app.route('/media/upload', methods=['POST'])
@login_required
def upload_media():
    try:
        if 'file' not in request.files:
            return jsonify({'success': False, 'error': 'No file selected'})
        
        file = request.files['file']
        if file.filename == '':
            return jsonify({'success': False, 'error': 'No file selected'})
        
        if file:
            filename = secure_filename(file.filename)
            filepath = os.path.join('uploads', filename)
            file.save(filepath)
            
            # Get file info
            file_size = os.path.getsize(filepath)
            
            # Get duration using ffprobe (simplified)
            duration = 0
            try:
                result = subprocess.run([
                    'ffprobe', '-v', 'quiet', '-show_entries', 'format=duration',
                    '-of', 'default=noprint_wrappers=1:nokey=1', filepath
                ], capture_output=True, text=True)
                duration = int(float(result.stdout.strip()))
            except:
                pass
            
            # Save to database
            conn = sqlite3.connect('database/iptv.db')
            c = conn.cursor()
            c.execute('''
                INSERT INTO media (filename, original_name, title, file_size, duration)
                VALUES (?, ?, ?, ?, ?)
            ''', (filename, file.filename, os.path.splitext(file.filename)[0], file_size, duration))
            conn.commit()
            conn.close()
            
            return jsonify({'success': True, 'message': 'File uploaded successfully'})
    
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})

@app.route('/media/delete/<int:media_id>', methods=['POST'])
@login_required
def delete_media(media_id):
    try:
        conn = sqlite3.connect('database/iptv.db')
        c = conn.cursor()
        
        # Get filename
        c.execute('SELECT filename FROM media WHERE id = ?', (media_id,))
        result = c.fetchone()
        
        if result:
            filename = result[0]
            filepath = os.path.join('uploads', filename)
            
            # Delete file
            if os.path.exists(filepath):
                os.remove(filepath)
            
            # Delete from database
            c.execute('DELETE FROM media WHERE id = ?', (media_id,))
            conn.commit()
        
        conn.close()
        return jsonify({'success': True, 'message': 'Media deleted successfully'})
    
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})

# Stream Management Routes
@app.route('/streams')
@login_required
def stream_management():
    conn = sqlite3.connect('database/iptv.db')
    c = conn.cursor()
    c.execute('SELECT * FROM streams')
    streams = c.fetchall()
    
    # Get media files for dropdown
    c.execute('SELECT id, filename, title FROM media ORDER BY title')
    media_files = c.fetchall()
    conn.close()
    
    # Get current status
    streams_with_status = []
    for stream in streams:
        status = stream_manager.get_stream_status(stream[0])
        streams_with_status.append({
            'id': stream[0],
            'name': stream[1],
            'input_source': stream[2],
            'output_url': stream[3],
            'db_status': stream[4],
            'actual_status': status
        })
    
    return render_template('streams.html', streams=streams_with_status, media_files=media_files)

@app.route('/stream/start/<int:stream_id>', methods=['POST'])
@login_required
def start_stream(stream_id):
    try:
        # Get media_id from request if provided
        media_id = request.json.get('media_id') if request.is_json else None
        
        conn = sqlite3.connect('database/iptv.db')
        c = conn.cursor()
        
        if media_id:
            # Start stream with specific media
            c.execute('SELECT filename FROM media WHERE id = ?', (media_id,))
            media = c.fetchone()
            if media:
                file_path = os.path.join('uploads', media[0])
                success, message = stream_manager.start_stream_with_file(stream_id, file_path)
            else:
                success, message = False, "Media not found"
        else:
            # Start default stream
            c.execute('SELECT input_source, output_url, config FROM streams WHERE id = ?', (stream_id,))
            stream = c.fetchone()
            if stream:
                success, message = stream_manager.start_stream(stream_id)
            else:
                success, message = False, "Stream not found"
        
        conn.close()
        return jsonify({'success': success, 'message': message})
    
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)})

@app.route('/stream/start_with_media/<int:stream_id>/<int:media_id>', methods=['POST'])
@login_required
def start_stream_with_media(stream_id, media_id):
    try:
        conn = sqlite3.connect('database/iptv.db')
        c = conn.cursor()
        c.execute('SELECT filename FROM media WHERE id = ?', (media_id,))
        media = c.fetchone()
        conn.close()
        
        if media:
            file_path = os.path.join('uploads', media[0])
            success, message = stream_manager.start_stream_with_file(stream_id, file_path)
            return jsonify({'success': success, 'message': message})
        else:
            return jsonify({'success': False, 'message': 'Media not found'})
    
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)})

@app.route('/stream/stop/<int:stream_id>', methods=['POST'])
@login_required
def stop_stream(stream_id):
    try:
        success, message = stream_manager.stop_stream(stream_id)
        return jsonify({'success': success, 'message': message})
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)})

@app.route('/stream/status/<int:stream_id>')
@login_required
def stream_status(stream_id):
    status = stream_manager.get_stream_status(stream_id)
    return jsonify(status)

# Scheduling Routes
@app.route('/schedules')
@login_required
def schedule_management():
    conn = sqlite3.connect('database/iptv.db')
    c = conn.cursor()
    c.execute('''
        SELECT s.*, m.title as media_title, st.name as stream_name
        FROM schedules s
        LEFT JOIN media m ON s.media_id = m.id
        LEFT JOIN streams st ON s.stream_id = st.id
        ORDER BY s.start_time DESC
    ''')
    schedules = c.fetchall()
    
    c.execute('SELECT id, title FROM media WHERE is_active = 1')
    media_files = c.fetchall()
    
    c.execute('SELECT id, name FROM streams')
    streams = c.fetchall()
    
    conn.close()
    
    return render_template('schedules.html', 
                         schedules=schedules, 
                         media_files=media_files, 
                         streams=streams)

@app.route('/schedule/create', methods=['POST'])
@login_required
def create_schedule():
    try:
        data = request.get_json()
        
        conn = sqlite3.connect('database/iptv.db')
        c = conn.cursor()
        c.execute('''
            INSERT INTO schedules (media_id, stream_id, start_time, end_time, repeat_type)
            VALUES (?, ?, ?, ?, ?)
        ''', (data['media_id'], data['stream_id'], data['start_time'], data['end_time'], data['repeat_type']))
        conn.commit()
        conn.close()
        
        return jsonify({'success': True, 'message': 'Schedule created successfully'})
    
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})

@app.route('/schedule/delete/<int:schedule_id>', methods=['POST'])
@login_required
def delete_schedule(schedule_id):
    try:
        conn = sqlite3.connect('database/iptv.db')
        c = conn.cursor()
        c.execute('DELETE FROM schedules WHERE id = ?', (schedule_id,))
        conn.commit()
        conn.close()
        
        return jsonify({'success': True, 'message': 'Schedule deleted successfully'})
    
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})

# Dashboard API
@app.route('/api/dashboard/stats')
@login_required
def dashboard_stats():
    conn = sqlite3.connect('database/iptv.db')
    c = conn.cursor()
    
    # Media count
    c.execute('SELECT COUNT(*) FROM media')
    media_count = c.fetchone()[0]
    
    # Active streams
    c.execute('SELECT COUNT(*) FROM streams WHERE status = "running"')
    active_streams = c.fetchone()[0]
    
    # Upcoming schedules
    c.execute('SELECT COUNT(*) FROM schedules WHERE start_time > ? AND is_active = 1', (datetime.now(),))
    upcoming_schedules = c.fetchone()[0]
    
    # Total storage used
    c.execute('SELECT SUM(file_size) FROM media')
    total_storage = c.fetchone()[0] or 0
    
    conn.close()
    
    return jsonify({
        'media_count': media_count,
        'active_streams': active_streams,
        'upcoming_schedules': upcoming_schedules,
        'total_storage': total_storage
    })

if __name__ == '__main__':
    create_directories()
    init_db()
    
    # Setup logging
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s %(levelname)s %(message)s',
        handlers=[
            logging.FileHandler('logs/app.log'),
            logging.StreamHandler()
        ]
    )
    
    app.run(host='0.0.0.0', port=5000, debug=True)