import os
import time
import asyncio
import aiosqlite
from quart import Quart, render_template, request, send_from_directory, Response
from datetime import datetime
from babel.dates import format_datetime
import locale
locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8')

app = Quart(__name__, template_folder='frontend', static_folder='frontend')

DB_PATH = 'maps.db'

GAME_MODES = {
    "Classic": "Классический",
    "Deathmatch": "Бой насмерть",
    "Demolition": "Уничтожение объекта",
    "Armsrace": "Гонка вооружений",
    "Custom": "Пользовательский",
    "Training": "Обучение",
    "Co-op Strike": "Совместный налёт",
    "Wingman": "Напарники",
    "Flying Scoutsman": "Перелётные снайперы"
}

last_download_times = {}
DOWNLOAD_COOLDOWN = 10

async def get_maps(page=1, per_page=30):
    async with aiosqlite.connect(DB_PATH) as conn:
        cursor = await conn.cursor()

        offset = (page - 1) * per_page

        await cursor.execute('''
            SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
            FROM maps
            ORDER BY DateTime DESC
            LIMIT ? OFFSET ?
        ''', (per_page, offset))

        maps = await cursor.fetchall()

    return maps

def get_image_path(filepath):
    image_path = os.path.join('J:/public/complete/workshop', filepath)
    if not os.path.exists(image_path):
        return "http://127.0.0.1:5000/images/image.jpg"
    return f"http://127.0.0.1:5000/images/{filepath.split('/')[0]}/{filepath.split('/')[1]}/{filepath.split('/')[1]}.jpg"

def get_star_image(stars):
    if stars is None or stars == 0:
        return "http://127.0.0.1:5000/stars/0-star.png"
    return f"http://127.0.0.1:5000/stars/{stars}-star.png"

@app.route('/images/<path:filename>')
async def serve_image(filename):
    image_path = os.path.join('J:/public/complete/workshop', filename)
    if os.path.exists(image_path):
        return await send_from_directory('J:/public/complete/workshop', filename)
    else:
        default_image_path = os.path.join('J:/public/complete/workshop', 'image.jpg')
        if os.path.exists(default_image_path):
            return await send_from_directory('J:/public/complete/workshop', 'image.jpg')
        return "Default image not found", 404

@app.route('/stars/<filename>')
async def serve_star_image(filename):
    star_path = os.path.join('J:/public/complete/workshop/stars', filename)
    if os.path.exists(star_path):
        return await send_from_directory('J:/public/complete/workshop/stars', filename)
    else:
        return "Star image not found", 404

@app.route('/download_bsp')
async def download_bsp():
    user_ip = request.remote_addr

    current_time = time.time()
    last_time = last_download_times.get(user_ip, 0)

    if current_time - last_time < DOWNLOAD_COOLDOWN:
        wait_time = DOWNLOAD_COOLDOWN - (current_time - last_time)
        return f"Please wait {int(wait_time)} seconds before downloading again.", 429

    last_download_times[user_ip] = current_time

    image_path = request.args.get('image_path')
    if not image_path:
        return "No image path provided", 400

    image_folder = os.path.dirname(image_path.replace("http://127.0.0.1:5000/images/", ""))
    bsp_filename = None

    for file in os.listdir(os.path.join('J:/public/complete/workshop', image_folder)):
        if file.endswith('.bsp'):
            bsp_filename = file
            break

    if not bsp_filename:
        return "No .bsp file found in the same directory", 404

    file_path = os.path.join('J:/public/complete/workshop', image_folder, bsp_filename)

    SPEED_LIMIT = 40 * 1024 * 1024 // 8

    async def file_stream():
        with open(file_path, 'rb') as f:
            while chunk := f.read(SPEED_LIMIT):
                yield chunk
                await asyncio.sleep(1)

    headers = {
        "Content-Disposition": f"attachment; filename={bsp_filename}"
    }
    return Response(file_stream(), headers=headers, content_type='application/octet-stream')

@app.route('/main')
async def main_page():
    image_url = request.args.get('image_url', 'default_image.jpg')
    map_title = request.args.get('map_title', 'Default Map Title')

    async with aiosqlite.connect(DB_PATH) as conn:
        cursor = await conn.cursor()
        await cursor.execute('''
            SELECT GameMode, Tags, FilePath, DateTime, YoutubeLink, Description
            FROM maps
            WHERE Title = ?
        ''', (map_title,))
        row = await cursor.fetchone()

    game_mode = row[0] if row else None
    tags = row[1] if row else None
    file_path = row[2] if row else None
    date_time = row[3] if row else None
    youtube_link = row[4] if row else None
    description = row[5] if row else "Нет описания"

    if date_time:
        dt = datetime.fromisoformat(date_time)
        added_time = format_datetime(dt, format='d MMM y г., HH:mm', locale='ru_RU')
    else:
        added_time = 'Не найдено'

    if game_mode:
        game_modes = game_mode.split(', ')
        game_modes = [GAME_MODES.get(mode, mode) for mode in game_modes]
        game_mode = ', '.join(game_modes)
    else:
        game_mode = 'Не найден'

    if tags:
        tags_list = tags.split(', ')
        tags_list = [GAME_MODES.get(tag, tag) for tag in tags_list]
        tags = ', '.join(tags_list)
    else:
        tags = 'Не найдено'

    if file_path:
        file_size = os.path.getsize(os.path.join('J:/public/complete/workshop', file_path))
        file_size_mb = file_size / (1024 * 1024)
        file_size_display = f"{file_size_mb:.2f} MB"
    else:
        file_size_display = 'Не найден'

    return await render_template(
        'main.html',
        image_url=image_url,
        map_title=map_title,
        game_mode=game_mode,
        tags=tags,
        file_size=file_size_display,
        added_time=added_time,
        youtube_link=youtube_link,
        description=description
    )


@app.route('/')
async def index():
    page = int(request.args.get('page', 1))
    selected_game_modes = request.args.getlist('game_modes')
    start_date = request.args.get('start_date')
    end_date = request.args.get('end_date')
    search_title = request.args.get('search_title')
    selected_stars = request.args.get('stars')

    maps_data = await get_maps_filtered(page, selected_game_modes, start_date, end_date, search_title, selected_stars)

    async with aiosqlite.connect(DB_PATH) as conn:
        cursor = await conn.cursor()

        query = 'SELECT COUNT(*) FROM maps WHERE 1=1'
        params = []

        if selected_game_modes:
            placeholders = ', '.join('?' for _ in selected_game_modes)
            query += f' AND GameMode IN ({placeholders})'
            params.extend(selected_game_modes)

        if start_date:
            query += ' AND DateTime >= ?'
            params.append(start_date)
        if end_date:
            query += ' AND DateTime <= ?'
            params.append(end_date)

        if search_title:
            query += ' AND Title LIKE ?'
            params.append(f'%{search_title}%')

        if selected_stars:
            query += ' AND Stars = ?'
            params.append(selected_stars)

        await cursor.execute(query, params)
        total_maps = await cursor.fetchone()

    total_maps = total_maps[0]
    per_page = 30
    total_pages = (total_maps + per_page - 1) // per_page

    filters = '&'.join(
        [f'game_modes={mode}' for mode in selected_game_modes] +
        ([f'start_date={start_date}'] if start_date else []) +
        ([f'end_date={end_date}'] if end_date else []) +
        ([f'search_title={search_title}'] if search_title else []) +
        ([f'stars={selected_stars}'] if selected_stars else [])
    )

    return await render_template(
        'workshop.html',
        maps_data=maps_data,
        page=page,
        total_pages=total_pages,
        get_image_path=get_image_path,
        get_star_image=get_star_image,
        selected_game_modes=selected_game_modes,
        filters=filters,
        selected_stars=selected_stars
    )

async def get_maps_filtered(page=1, selected_game_modes=None, start_date=None, end_date=None, search_title=None, selected_stars=None):
    async with aiosqlite.connect(DB_PATH) as conn:
        cursor = await conn.cursor()

        offset = (page - 1) * 30
        query = '''
            SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
            FROM maps
            WHERE 1=1
        '''
        params = []

        if selected_stars:
            query += ' AND Stars = ?'
            params.append(selected_stars)

        if search_title:
            query += ' AND Title LIKE ?'
            params.append(f'%{search_title}%')

        if selected_game_modes:
            query += ' AND GameMode IN ({})'.format(','.join('?' for _ in selected_game_modes))
            params.extend(selected_game_modes)

        if start_date:
            query += ' AND DateTime >= ?'
            params.append(start_date)
        if end_date:
            query += ' AND DateTime <= ?'
            params.append(end_date)

        query += ' ORDER BY DateTime DESC LIMIT ? OFFSET ?'
        params.extend([30, offset])

        await cursor.execute(query, params)
        maps = await cursor.fetchall()

    return maps

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000)
