from __future__ import annotations

import json
import os
import sqlite3
from datetime import datetime, timezone
from functools import wraps
from pathlib import Path

from flask import Blueprint, abort, flash, redirect, render_template, request, send_file, session, url_for
from werkzeug.security import check_password_hash, generate_password_hash

from .config import BASE_DIR, DB_PATH, UPLOAD_DIR
from .db import execute, get_bool, get_int, get_setting, query_all, query_one, set_setting, transaction, utcnow
from .services.automation_engine import AutomationEngine
from .services.backup_service import BackupService
from .services.debug_service import DebugService
from .services.errors import ERROR_MESSAGES
from .services.instagram_service import InstagramService
from .services.log_service import activity, cleanup_old_logs, error
from .services.media_service import MediaService
from .services.normalizer import TextNormalizer
from .services.scheduler_service import SchedulerService
from .services.worker_service import WorkerService

bp = Blueprint('main', __name__)


def admin_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if not session.get('admin_id'):
            return redirect(url_for('main.login', next=request.path))
        return view(*args, **kwargs)
    return wrapped


def _setup_complete() -> bool:
    return get_bool('setup_complete', False)


@bp.before_app_request
def setup_guard():
    endpoint = request.endpoint or ''
    allowed = {'main.setup', 'main.login', 'main.logout', 'main.healthz', 'static'}
    if not _setup_complete() and endpoint not in allowed and not endpoint.startswith('static'):
        return redirect(url_for('main.setup', step=1))


@bp.route('/healthz')
def healthz():
    return {'ok': True}


@bp.route('/setup', methods=['GET', 'POST'])
def setup():
    step = max(1, min(int(request.args.get('step', 1)), 6))
    # As soon as an admin exists, the remaining setup screens become private.
    # This avoids exposing server paths or allowing an unauthenticated account
    # connection if setup was interrupted after the admin step.
    if query_one('SELECT 1 FROM admins LIMIT 1') and not session.get('admin_id'):
        return redirect(url_for('main.login', next=url_for('main.setup', step=step)))
    debug_service = DebugService()
    ig = InstagramService()
    scheduler = SchedulerService()
    result = None

    if request.method == 'POST':
        action = request.form.get('action')
        if action == 'create_admin':
            username = request.form.get('username', '').strip()
            password = request.form.get('password', '')
            if len(username) < 3 or len(password) < 8:
                flash('\u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc \u0645\u062f\u06cc\u0631 \u0628\u0627\u06cc\u062f \u062d\u062f\u0627\u0642\u0644 \u06f3 \u06a9\u0627\u0631\u0627\u06a9\u062a\u0631 \u0648 \u0631\u0645\u0632 \u0639\u0628\u0648\u0631 \u062d\u062f\u0627\u0642\u0644 \u06f8 \u06a9\u0627\u0631\u0627\u06a9\u062a\u0631 \u0628\u0627\u0634\u062f.', 'danger')
            elif query_one('SELECT 1 FROM admins LIMIT 1'):
                flash('\u0645\u062f\u06cc\u0631 \u0642\u0628\u0644\u0627\u064b \u0633\u0627\u062e\u062a\u0647 \u0634\u062f\u0647 \u0627\u0633\u062a.', 'warning')
            else:
                admin_id = execute('INSERT INTO admins(username, password_hash, created_at) VALUES (?, ?, ?)', (username, generate_password_hash(password), utcnow()))
                session['admin_id'] = admin_id
                session.permanent = True
                return redirect(url_for('main.setup', step=3))
        elif action == 'instagram_connect':
            result = ig.connect(request.form.get('ig_username', ''), request.form.get('ig_password', ''), request.form.get('verification_code') or None)
            if result['ok']:
                flash('\u0627\u062a\u0635\u0627\u0644 \u0627\u06cc\u0646\u0633\u062a\u0627\u06af\u0631\u0627\u0645 \u0628\u0627 \u0645\u0648\u0641\u0642\u06cc\u062a \u0627\u0646\u062c\u0627\u0645 \u0634\u062f \u0648 Session \u0630\u062e\u06cc\u0631\u0647 \u0634\u062f.', 'success')
                return redirect(url_for('main.setup', step=5))
            flash(f"Instagram: {result.get('type')} - {result.get('message')}", 'danger')
        elif action == 'finish':
            if not query_one('SELECT 1 FROM admins LIMIT 1'):
                flash('\u0627\u0628\u062a\u062f\u0627 \u0645\u062f\u06cc\u0631 \u067e\u0646\u0644 \u0631\u0627 \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f.', 'danger')
                return redirect(url_for('main.setup', step=2))
            set_setting('setup_complete', '1')
            flash('\u0631\u0627\u0647\u200c\u0627\u0646\u062f\u0627\u0632\u06cc \u0627\u0648\u0644\u06cc\u0647 \u0628\u0627 \u0645\u0648\u0641\u0642\u06cc\u062a \u062a\u06a9\u0645\u06cc\u0644 \u0634\u062f.', 'success')
            return redirect(url_for('main.dashboard'))

    checks = debug_service.server_checks(request.is_secure) if step == 1 else []
    diagnostics = debug_service.self_diagnostics(request.is_secure, online_instagram=False) if step == 6 else []
    return render_template('setup.html', step=step, checks=checks, diagnostics=diagnostics, result=result, account=ig.account_row(), scheduler=scheduler)


@bp.route('/login', methods=['GET', 'POST'])
def login():
    if session.get('admin_id'):
        return redirect(url_for('main.dashboard'))
    if request.method == 'POST':
        username = request.form.get('username', '').strip()
        password = request.form.get('password', '')
        remote = request.remote_addr or 'unknown'
        recent = query_one(
            "SELECT COUNT(*) AS c FROM login_attempts WHERE remote_addr=? AND success=0 AND datetime(attempted_at) >= datetime('now','-15 minutes')",
            (remote,),
        )
        if recent and recent['c'] >= 5:
            flash('\u062a\u0639\u062f\u0627\u062f \u062a\u0644\u0627\u0634 \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0631\u0627\u06cc \u0648\u0631\u0648\u062f \u0632\u06cc\u0627\u062f \u0627\u0633\u062a. \u06a9\u0645\u06cc \u0628\u0639\u062f \u062f\u0648\u0628\u0627\u0631\u0647 \u0627\u0645\u062a\u062d\u0627\u0646 \u06a9\u0646\u06cc\u062f.', 'danger')
            return render_template('login.html'), 429
        admin = query_one('SELECT * FROM admins WHERE username=?', (username,))
        ok = bool(admin and check_password_hash(admin['password_hash'], password))
        execute('INSERT INTO login_attempts(remote_addr, username, success, attempted_at) VALUES (?, ?, ?, ?)', (remote, username, 1 if ok else 0, utcnow()))
        if ok:
            execute('DELETE FROM login_attempts WHERE remote_addr=? AND success=0', (remote,))
            execute('UPDATE admins SET last_login_at=? WHERE id=?', (utcnow(), admin['id']))
            session.clear()
            session['admin_id'] = admin['id']
            session.permanent = True
            next_url = request.args.get('next', '')
            if not next_url.startswith('/') or next_url.startswith('//'):
                next_url = url_for('main.dashboard')
            return redirect(next_url)
        flash('\u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc \u06cc\u0627 \u0631\u0645\u0632 \u0639\u0628\u0648\u0631 \u0635\u062d\u06cc\u062d \u0646\u06cc\u0633\u062a.', 'danger')
    return render_template('login.html')


@bp.route('/logout', methods=['POST'])
@admin_required
def logout():
    session.clear()
    return redirect(url_for('main.login'))


@bp.route('/')
@admin_required
def dashboard():
    account = query_one('SELECT * FROM instagram_accounts WHERE id=1')
    scheduler = SchedulerService().status()
    latest_worker = query_one('SELECT * FROM worker_runs ORDER BY id DESC LIMIT 1')
    counts = {
        'comments_today': query_one("SELECT COUNT(*) AS c FROM processed_comments WHERE date(processed_at)=date('now')")['c'],
        'dm_today': query_one("SELECT COUNT(*) AS c FROM processed_messages WHERE date(processed_at)=date('now') AND reply_status IN ('sent','resumed','dry_run','processed')")['c'],
        'errors_today': query_one("SELECT COUNT(*) AS c FROM error_logs WHERE date(created_at)=date('now')")['c'],
        'active_automations': query_one('SELECT COUNT(*) AS c FROM automations WHERE enabled=1')['c'],
    }
    activities = query_all('SELECT * FROM activity_logs ORDER BY id DESC LIMIT 12')
    errors = query_all('SELECT * FROM error_logs ORDER BY id DESC LIMIT 8')
    return render_template('dashboard.html', account=account, scheduler=scheduler, latest_worker=latest_worker, counts=counts, activities=activities, errors=errors, debug_mode=get_bool('debug_mode'), dry_run=get_bool('dry_run'))


@bp.route('/bot/toggle', methods=['POST'])
@admin_required
def bot_toggle():
    enabled = request.form.get('enabled') == '1'
    set_setting('bot_enabled', '1' if enabled else '0')
    activity('system', 'bot_toggle', 'Bot enabled' if enabled else 'Bot disabled')
    flash('\u0631\u0628\u0627\u062a \u0641\u0639\u0627\u0644 \u0634\u062f.' if enabled else '\u0631\u0628\u0627\u062a \u0645\u062a\u0648\u0642\u0641 \u0634\u062f. Worker \u0641\u0642\u0637 \u0648\u0636\u0639\u06cc\u062a \u0631\u0627 \u062b\u0628\u062a \u0645\u06cc\u200c\u06a9\u0646\u062f \u0648 \u067e\u06cc\u0627\u0645 \u062c\u062f\u06cc\u062f\u06cc \u0627\u0631\u0633\u0627\u0644 \u0646\u0645\u06cc\u200c\u06a9\u0646\u062f.', 'success' if enabled else 'warning')
    return redirect(request.referrer or url_for('main.dashboard'))


@bp.route('/instagram', methods=['GET', 'POST'])
@admin_required
def instagram():
    service = InstagramService()
    result = None
    medias = []
    if request.method == 'POST':
        action = request.form.get('action')
        if action == 'connect':
            result = service.connect(request.form.get('username', ''), request.form.get('password', ''), request.form.get('verification_code') or None)
            flash('Instagram connected.' if result['ok'] else f"{result.get('type')}: {result.get('message')}", 'success' if result['ok'] else 'danger')
        elif action == 'test':
            result = service.test_connection()
            flash('Instagram connection OK.' if result['ok'] else f"{result.get('type')}: {result.get('message')}", 'success' if result['ok'] else 'danger')
    account = service.account_row()
    if account and account['status'] == 'connected':
        try:
            medias = service.recent_media(amount=8)
        except Exception:
            medias = []
    return render_template('instagram.html', account=account, result=result, medias=medias)


def _parse_step_config(index: int) -> tuple[str, dict]:
    types = request.form.getlist('step_type')
    texts = request.form.getlist('step_text')
    keywords = request.form.getlist('step_keywords')
    media_ids = request.form.getlist('step_media_file_id')
    min_seconds = request.form.getlist('step_min_seconds')
    max_seconds = request.form.getlist('step_max_seconds')
    not_followed = request.form.getlist('step_not_followed_text')
    unknown = request.form.getlist('step_unknown_text')
    step_type = types[index]
    def item(values, default=''):
        return values[index] if index < len(values) else default
    cfg = {}
    if step_type in {'send_text_dm', 'send_link', 'send_comment_reply'}:
        if item(texts).strip():
            cfg['text'] = item(texts).strip()
    if step_type == 'wait_for_user_message':
        cfg['keywords'] = [x.strip() for x in item(keywords).replace(',', '\n').splitlines() if x.strip()]
    if step_type in {'send_image_dm', 'send_video_dm'}:
        cfg['media_file_id'] = int(item(media_ids, '0') or 0)
    if step_type == 'delay':
        cfg['min_seconds'] = float(item(min_seconds, '1') or 1)
        cfg['max_seconds'] = float(item(max_seconds, item(min_seconds, '1')) or 1)
    if step_type == 'check_follow':
        cfg['not_followed_text'] = item(not_followed).strip()
        cfg['unknown_text'] = item(unknown).strip()
        cfg['retry_keywords'] = [x.strip() for x in item(keywords).replace(',', '\n').splitlines() if x.strip()]
    return step_type, cfg


def _save_automation(automation_id: int | None = None) -> int:
    name = request.form.get('name', '').strip()
    if not name:
        raise ValueError('\u0646\u0627\u0645 \u0627\u062a\u0648\u0645\u0627\u0633\u06cc\u0648\u0646 \u0627\u0644\u0632\u0627\u0645\u06cc \u0627\u0633\u062a.')
    trigger_type = request.form.get('trigger_type', 'comment')
    if trigger_type not in {'comment', 'dm', 'both'}:
        raise ValueError('\u0646\u0648\u0639 Trigger \u0645\u0639\u062a\u0628\u0631 \u0646\u06cc\u0633\u062a.')
    media_scope = request.form.get('media_scope', 'all')
    reply_mode = request.form.get('reply_mode', 'random')
    enabled = 1 if request.form.get('enabled') else 0
    now = utcnow()
    with transaction() as conn:
        if automation_id:
            conn.execute('UPDATE automations SET name=?, enabled=?, trigger_type=?, media_scope=?, reply_mode=?, updated_at=? WHERE id=?', (name, enabled, trigger_type, media_scope, reply_mode, now, automation_id))
            conn.execute('DELETE FROM automation_keywords WHERE automation_id=?', (automation_id,))
            conn.execute('DELETE FROM automation_steps WHERE automation_id=?', (automation_id,))
            conn.execute('DELETE FROM comment_replies WHERE automation_id=?', (automation_id,))
            conn.execute('DELETE FROM automation_media WHERE automation_id=?', (automation_id,))
        else:
            cur = conn.execute('INSERT INTO automations(name, enabled, trigger_type, media_scope, reply_mode, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)', (name, enabled, trigger_type, media_scope, reply_mode, now, now))
            automation_id = cur.lastrowid

        for line in request.form.get('keywords_text', '').splitlines():
            line = line.strip()
            if not line:
                continue
            if '|' in line:
                keyword, match_type = [part.strip() for part in line.split('|', 1)]
            else:
                keyword, match_type = line, 'exact'
            if match_type not in {'exact', 'contains', 'starts_with', 'ends_with'}:
                match_type = 'exact'
            conn.execute('INSERT INTO automation_keywords(automation_id, keyword, match_type) VALUES (?, ?, ?)', (automation_id, keyword, match_type))

        for pos, reply in enumerate(request.form.get('comment_replies_text', '').splitlines()):
            if reply.strip():
                conn.execute('INSERT INTO comment_replies(automation_id, text, position) VALUES (?, ?, ?)', (automation_id, reply.strip(), pos))

        types = request.form.getlist('step_type')
        for pos in range(len(types)):
            step_type, cfg = _parse_step_config(pos)
            conn.execute('INSERT INTO automation_steps(automation_id, position, step_type, config_json) VALUES (?, ?, ?, ?)', (automation_id, pos, step_type, json.dumps(cfg, ensure_ascii=False)))

        for media_id in request.form.getlist('media_ids'):
            conn.execute('INSERT OR IGNORE INTO automation_media(automation_id, media_id) VALUES (?, ?)', (automation_id, media_id))
    return int(automation_id)


@bp.route('/automations')
@admin_required
def automations():
    rows = query_all('SELECT a.*, (SELECT COUNT(*) FROM automation_keywords k WHERE k.automation_id=a.id) AS keyword_count FROM automations a ORDER BY id DESC')
    return render_template('automations.html', automations=rows)


@bp.route('/automations/new', methods=['GET', 'POST'])
@admin_required
def automation_new():
    if request.method == 'POST':
        try:
            automation_id = _save_automation()
            flash('\u0627\u062a\u0648\u0645\u0627\u0633\u06cc\u0648\u0646 \u0630\u062e\u06cc\u0631\u0647 \u0634\u062f.', 'success')
            return redirect(url_for('main.automation_edit', automation_id=automation_id))
        except Exception as exc:
            flash(str(exc), 'danger')
    media_files = query_all('SELECT * FROM media_files ORDER BY id DESC')
    recent_media = []
    try:
        recent_media = InstagramService().recent_media(amount=8)
    except Exception:
        pass
    return render_template('automation_form.html', automation=None, keywords_text='', replies_text='', steps=[], media_files=media_files, recent_media=recent_media, selected_media=[])


@bp.route('/automations/<int:automation_id>/edit', methods=['GET', 'POST'])
@admin_required
def automation_edit(automation_id):
    automation = query_one('SELECT * FROM automations WHERE id=?', (automation_id,))
    if not automation:
        abort(404)
    if request.method == 'POST':
        try:
            _save_automation(automation_id)
            flash('\u0627\u062a\u0648\u0645\u0627\u0633\u06cc\u0648\u0646 \u0628\u0647\u200c\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0634\u062f.', 'success')
            return redirect(url_for('main.automation_edit', automation_id=automation_id))
        except Exception as exc:
            flash(str(exc), 'danger')
    keywords = query_all('SELECT * FROM automation_keywords WHERE automation_id=? ORDER BY id', (automation_id,))
    replies = query_all('SELECT * FROM comment_replies WHERE automation_id=? ORDER BY position, id', (automation_id,))
    steps = [dict(s) | {'config': json.loads(s['config_json'] or '{}')} for s in query_all('SELECT * FROM automation_steps WHERE automation_id=? ORDER BY position,id', (automation_id,))]
    selected_media = [r['media_id'] for r in query_all('SELECT media_id FROM automation_media WHERE automation_id=?', (automation_id,))]
    media_files = query_all('SELECT * FROM media_files ORDER BY id DESC')
    recent_media = []
    try:
        recent_media = InstagramService().recent_media(amount=8)
    except Exception:
        pass
    keywords_text = '\n'.join(f"{r['keyword']}|{r['match_type']}" for r in keywords)
    replies_text = '\n'.join(r['text'] for r in replies)
    return render_template('automation_form.html', automation=automation, keywords_text=keywords_text, replies_text=replies_text, steps=steps, media_files=media_files, recent_media=recent_media, selected_media=selected_media)


@bp.route('/automations/<int:automation_id>/delete', methods=['POST'])
@admin_required
def automation_delete(automation_id):
    execute('DELETE FROM automations WHERE id=?', (automation_id,))
    flash('\u0627\u062a\u0648\u0645\u0627\u0633\u06cc\u0648\u0646 \u062d\u0630\u0641 \u0634\u062f.', 'success')
    return redirect(url_for('main.automations'))


@bp.route('/media', methods=['GET', 'POST'])
@admin_required
def media():
    service = MediaService()
    if request.method == 'POST':
        try:
            service.save(request.files.get('file'))
            flash('\u0641\u0627\u06cc\u0644 \u0628\u0627 \u0645\u0648\u0641\u0642\u06cc\u062a \u0622\u067e\u0644\u0648\u062f \u0634\u062f.', 'success')
        except Exception as exc:
            error(exc, 'MediaService', 'upload', error_type='MEDIA_UPLOAD_ERROR')
            flash(str(exc), 'danger')
    files = query_all('SELECT * FROM media_files ORDER BY id DESC')
    return render_template('media.html', files=files)


@bp.route('/media/<int:media_id>/delete', methods=['POST'])
@admin_required
def media_delete(media_id):
    row = query_one('SELECT * FROM media_files WHERE id=?', (media_id,))
    if row:
        path = (UPLOAD_DIR / row['stored_name']).resolve()
        if UPLOAD_DIR.resolve() in path.parents:
            path.unlink(missing_ok=True)
        execute('DELETE FROM media_files WHERE id=?', (media_id,))
    return redirect(url_for('main.media'))


@bp.route('/messages')
@admin_required
def messages():
    comments = query_all('SELECT * FROM processed_comments ORDER BY processed_at DESC LIMIT 100')
    dms = query_all('SELECT * FROM processed_messages ORDER BY processed_at DESC LIMIT 100')
    follows = query_all('SELECT * FROM follow_checks ORDER BY id DESC LIMIT 100')
    return render_template('messages.html', comments=comments, dms=dms, follows=follows)


@bp.route('/logs', methods=['GET', 'POST'])
@admin_required
def logs():
    if request.method == 'POST' and request.form.get('confirm') == 'yes':
        cleanup_old_logs()
        flash('Log\u0647\u0627\u06cc \u0642\u062f\u06cc\u0645\u06cc \u067e\u0627\u06a9\u200c\u0633\u0627\u0632\u06cc \u0634\u062f\u0646\u062f.', 'success')
    level = request.args.get('level', '')
    category = request.args.get('category', '')
    search = request.args.get('q', '').strip()
    sql = 'SELECT * FROM activity_logs WHERE 1=1'
    params = []
    if level:
        sql += ' AND level=?'; params.append(level)
    if category:
        sql += ' AND category=?'; params.append(category)
    if search:
        sql += ' AND (message LIKE ? OR details_json LIKE ?)'; params.extend([f'%{search}%', f'%{search}%'])
    sql += ' ORDER BY id DESC LIMIT 300'
    rows = query_all(sql, params)
    return render_template('logs.html', logs=rows)


@bp.route('/errors')
@admin_required
def errors():
    rows = query_all('SELECT * FROM error_logs ORDER BY id DESC LIMIT 300')
    return render_template('errors.html', errors=rows, error_messages=ERROR_MESSAGES, debug_mode=get_bool('debug_mode'))


@bp.route('/debug', methods=['GET', 'POST'])
@admin_required
def debug_page():
    service = DebugService()
    result = None
    normalizer = None
    automation_test = None
    if request.method == 'POST':
        action = request.form.get('action')
        if action == 'normalize':
            normalizer = TextNormalizer.explain(request.form.get('text', ''))
        elif action == 'automation_test':
            automation_test = AutomationEngine().dry_run_match(request.form.get('trigger_type', 'comment'), request.form.get('text', ''), request.form.get('media_id') or None)
        elif action == 'instagram_test':
            result = InstagramService().test_connection()
        elif action == 'diagnostics':
            result = {'diagnostics': service.self_diagnostics(request.is_secure, online_instagram=request.form.get('online') == '1')}
        elif action == 'db_integrity':
            result = {'database': service.database_info()}
        elif action in {'test_text', 'test_image', 'test_video'}:
            if request.form.get('confirm') != 'yes':
                result = {'ok': False, 'message': '\u0628\u0631\u0627\u06cc \u0627\u0631\u0633\u0627\u0644 \u0648\u0627\u0642\u0639\u06cc \u0628\u0627\u06cc\u062f \u06af\u0632\u06cc\u0646\u0647 \u062a\u0623\u06cc\u06cc\u062f \u0631\u0627 \u0641\u0639\u0627\u0644 \u06a9\u0646\u06cc\u062f.'}
            else:
                try:
                    client = InstagramService().load_client()
                    user_id = request.form.get('user_id', '').strip()
                    if action == 'test_text':
                        InstagramService().send_text(client, user_id, request.form.get('test_text', 'Test message'))
                    else:
                        path = MediaService().path_for(int(request.form.get('media_file_id', '0')))
                        if action == 'test_image':
                            InstagramService().send_photo(client, user_id, path)
                        else:
                            InstagramService().send_video(client, user_id, path)
                    result = {'ok': True, 'message': '\u067e\u06cc\u0627\u0645 \u062a\u0633\u062a\u06cc \u0627\u0631\u0633\u0627\u0644 \u0634\u062f.'}
                except Exception as exc:
                    et = error(exc, 'DebugPage', action, user_id=request.form.get('user_id'))
                    result = {'ok': False, 'message': f'{et}: {exc}'}
    data = {
        'system': service.system_info(request.is_secure),
        'instagram': service.instagram_info(),
        'database': service.database_info(),
        'scheduler': SchedulerService().status(),
        'worker_runs': query_all('SELECT * FROM worker_runs ORDER BY id DESC LIMIT 20'),
        'debug_logs': query_all('SELECT * FROM debug_logs ORDER BY id DESC LIMIT 100'),
        'recent_errors': query_all('SELECT * FROM error_logs ORDER BY id DESC LIMIT 20'),
        'comments': query_all('SELECT * FROM processed_comments ORDER BY processed_at DESC LIMIT 20'),
        'dms': query_all('SELECT * FROM processed_messages ORDER BY processed_at DESC LIMIT 20'),
        'follows': query_all('SELECT * FROM follow_checks ORDER BY id DESC LIMIT 20'),
        'media_files': query_all('SELECT * FROM media_files ORDER BY id DESC'),
    }
    return render_template('debug.html', data=data, result=result, normalizer=normalizer, automation_test=automation_test, debug_mode=get_bool('debug_mode'), dry_run=get_bool('dry_run'))


@bp.route('/debug/report', methods=['POST'])
@admin_required
def debug_report():
    path = DebugService().create_debug_report()
    return send_file(path, as_attachment=True, download_name=path.name)


@bp.route('/scheduler', methods=['GET', 'POST'])
@admin_required
def scheduler():
    service = SchedulerService()
    result = None
    if request.method == 'POST':
        action = request.form.get('action')
        if action == 'run_worker':
            result = WorkerService().run()
        elif action == 'create_cron':
            result = service.create_cpanel_cron()
        elif action == 'start_background':
            result = service.start_background()
        elif action == 'stop_background':
            result = service.stop_background()
    return render_template('scheduler.html', scheduler=service.status(), background=service.background_status(), cron_command=service.cron_command(), cron_expression=service.cron_expression(), uapi=service.cpanel_uapi_available(), result=result)


@bp.route('/health')
@admin_required
def health():
    return render_template('health.html', health=DebugService().health())


@bp.route('/settings', methods=['GET', 'POST'])
@admin_required
def settings():
    keys = ['bot_enabled', 'debug_mode', 'dry_run', 'comment_check_limit', 'dm_check_limit', 'posts_check_limit', 'message_delay_min', 'message_delay_max', 'follow_cache_duration', 'max_follow_checks', 'follow_check_window', 'log_retention_days', 'scheduler_mode']
    if request.method == 'POST':
        for key in keys:
            if key in {'bot_enabled', 'debug_mode', 'dry_run'}:
                set_setting(key, '1' if request.form.get(key) else '0')
            else:
                value = request.form.get(key)
                if value is not None:
                    set_setting(key, value)
        flash('\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0630\u062e\u06cc\u0631\u0647 \u0634\u062f.', 'success')
    values = {key: get_setting(key) for key in keys}
    return render_template('settings.html', values=values)


@bp.route('/backup', methods=['GET', 'POST'])
@admin_required
def backup():
    service = BackupService()
    if request.method == 'POST':
        path = service.create()
        flash(f'Backup created: {path.name}', 'success')
    return render_template('backup.html', backups=service.list())


@bp.route('/backup/<name>')
@admin_required
def backup_download(name):
    service = BackupService()
    target = (service.backup_dir / name).resolve()
    if service.backup_dir.resolve() not in target.parents or not target.exists() or target.suffix != '.sqlite3':
        abort(404)
    return send_file(target, as_attachment=True, download_name=target.name)


@bp.app_errorhandler(413)
def too_large(_err):
    flash('\u062d\u062c\u0645 \u0641\u0627\u06cc\u0644 \u0628\u06cc\u0634\u062a\u0631 \u0627\u0632 \u06f2\u06f5 \u0645\u06af\u0627\u0628\u0627\u06cc\u062a \u0627\u0633\u062a.', 'danger')
    return redirect(url_for('main.media'))


@bp.app_errorhandler(500)
def internal_error(err):
    try:
        error(err, 'Flask', request.path)
    except Exception:
        pass
    return render_template('error_public.html'), 500
