import os
import time
import sys
from flask import Flask, Response, jsonify
from gevent.queue import Queue
from gevent import monkey, spawn
import sqlite3

# جلوگیری از خطای Unicode در خروجی
sys.stdout.reconfigure(encoding='ascii', errors='ignore')

monkey.patch_all()
app = Flask(__name__)

# ===== DATABASE =====
DB_PATH = os.path.join(os.path.dirname(__file__), 'radio_config.db')

def init_db():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute('''
        CREATE TABLE IF NOT EXISTS channels (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            folder TEXT NOT NULL UNIQUE,
            enabled INTEGER DEFAULT 1
        )
    ''')
    conn.commit()
    conn.close()

def get_enabled_channels():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT id, name, folder FROM channels WHERE enabled = 1 ORDER BY id")
    rows = c.fetchall()
    conn.close()
    return [{'id': r[0], 'name': r[1], 'folder': r[2]} for r in rows]

def is_configured():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT COUNT(*) FROM channels WHERE enabled = 1")
    count = c.fetchone()[0]
    conn.close()
    return count > 0

# ===== RADIO CLASS =====
class Radio:
    def __init__(self, playlist):
        self.playlist = playlist
        self.current_song_index = 0

    def next(self):
        self.current_song_index = (self.current_song_index + 1) % len(self.playlist)
        return self.playlist[self.current_song_index]

    def prev(self):
        self.current_song_index = (self.current_song_index - 1) % len(self.playlist)
        return self.playlist[self.current_song_index]

    def current(self):
        return self.playlist[self.current_song_index]

    def audio_stream(self, chunk_queue):
        while True:
            file_path = self.playlist[self.current_song_index]
            try:
                with open(file_path, "rb") as f:
                    while chunk := f.read(4096):
                        if chunk_queue.full():
                            chunk_queue.get()
                        chunk_queue.put(chunk)
                        time.sleep(0.05)
            except:
                pass
            self.current_song_index = (self.current_song_index + 1) % len(self.playlist)

# ===== HELPER FUNCTIONS =====
def ensure_channel_folder(folder_path):
    os.makedirs(folder_path, exist_ok=True)
    mp3_files = [f for f in os.listdir(folder_path) if f.endswith('.mp3')]
    if not mp3_files:
        sample = os.path.join(folder_path, '_silent.mp3')
        if not os.path.exists(sample):
            with open(sample, 'wb') as f:
                f.write(b'\xFF\xFB\x90\x00\x00\x00\x00\x00')
        mp3_files = [sample]
    return sorted([os.path.join(folder_path, f) for f in mp3_files])

def start_channel(ch_id, ch_name, folder_name):
    full_path = os.path.join(os.path.dirname(__file__), "channels", folder_name)
    playlist = ensure_channel_folder(full_path)
    radio = Radio(playlist)
    chunk_queue = Queue(maxsize=10)
    clients = []
    spawn(radio.audio_stream, chunk_queue)
    def broadcast():
        while True:
            chunk = chunk_queue.get()
            for q in clients[:]:
                q.put(chunk)
    spawn(broadcast)
    return {
        'id': ch_id,
        'name': ch_name,
        'queue': chunk_queue,
        'clients': clients,
        'radio': radio
    }

active_channels = {}

def load_channels():
    global active_channels
    active_channels.clear()
    channels_db = get_enabled_channels()
    for ch in channels_db:
        try:
            data = start_channel(ch['id'], ch['name'], ch['folder'])
            active_channels[ch['id']] = data
        except Exception as e:
            pass

def client_generator(ch_data):
    q = Queue()
    ch_data['clients'].append(q)
    try:
        while True:
            yield q.get()
    finally:
        ch_data['clients'].remove(q)

# ===== WEB ROUTES =====
@app.route('/')
def index():
    html = '''
    <!DOCTYPE html>
    <html dir="rtl" lang="fa">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Radio Station</title>
        <style>
            body{font-family:Tahoma;background:#0f1724;color:white;text-align:center;padding:2rem}
            .channels{display:flex;flex-wrap:wrap;gap:20px;justify-content:center;margin:40px 0}
            .card{background:#2c3e66;border:none;border-radius:32px;padding:20px;width:180px;cursor:pointer;transition:0.2s;font-family:inherit;font-size:inherit;color:white;text-align:center}
            .card:hover{background:#e67e22}
            .card.active{background:#e67e22;box-shadow:0 0 15px orange}
            audio{width:80%;margin-top:30px}
            .status{margin-top:20px;font-size:1.1rem}
        </style>
    </head>
    <body>
        <h1>Radio Station</h1>
        <div class="channels" id="channelsContainer">Loading...</div>
        <audio id="player" controls></audio>
        <div id="status" class="status">Click on a button</div>
        <script>
            async function loadChannels(){
                try{
                    const res=await fetch('/api/channels');
                    const channels=await res.json();
                    const container=document.getElementById('channelsContainer');
                    container.innerHTML='';
                    for(const[id,data]of Object.entries(channels)){
                        const btn=document.createElement('button');
                        btn.className='card';
                        btn.setAttribute('data-id',id);
                        btn.innerText=data.name;
                        btn.onclick=()=>playChannel(id,data.name);
                        container.appendChild(btn);
                    }
                    if(Object.keys(channels).length===0){
                        container.innerHTML='<div>No active channels</div>';
                    }
                }catch(e){
                    document.getElementById('channelsContainer').innerHTML='<div style="color:red;">Connection error</div>';
                }
            }
            const player=document.getElementById('player');
            const statusDiv=document.getElementById('status');
            let activeId=null;
            function playChannel(id,name){
                if(activeId===id && !player.paused){
                    player.pause();
                    player.src='';
                    player.load();
                    activeId=null;
                    statusDiv.innerText='Stopped';
                    document.querySelectorAll('.card').forEach(c=>c.classList.remove('active'));
                    return;
                }
                player.src=`/radio/${id}`;
                player.play().catch(e=>console.log);
                activeId=id;
                statusDiv.innerText=`Playing: ${name}`;
                document.querySelectorAll('.card').forEach(c=>{
                    if(c.getAttribute('data-id')==id) c.classList.add('active');
                    else c.classList.remove('active');
                });
            }
            loadChannels();
            setInterval(loadChannels,15000);
        </script>
    </body>
    </html>
    '''
    return html

@app.route('/api/channels')
def api_channels():
    data = {str(ch_id): {'name': ch_data['name']} for ch_id, ch_data in active_channels.items()}
    return jsonify(data)

@app.route('/radio/<int:ch_id>')
def radio_stream(ch_id):
    if ch_id not in active_channels:
        return "Channel not found", 404
    return Response(client_generator(active_channels[ch_id]), mimetype='audio/mpeg')

# ===== INITIALIZATION =====
init_db()

if not is_configured():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("INSERT INTO channels (name, folder, enabled) VALUES (?, ?, ?)", ('Radio 1', 'channel_1', 1))
    conn.commit()
    conn.close()
    os.makedirs(os.path.join(os.path.dirname(__file__), "channels", "channel_1"), exist_ok=True)

load_channels()

application = app

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)