import os
from datetime import datetime
from functools import wraps

from flask import (Flask, render_template, request, redirect, url_for, flash,
                   abort, session, jsonify, current_app)
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from flask_migrate import Migrate
from flask_wtf.csrf import CSRFProtect
from slugify import slugify

from config import config
from models import db, User, Category, Tag, Article, SiteSettings, article_tags
from forms import LoginForm, UserForm, ArticleForm, CategoryForm, SiteSettingsForm
from utils import sanitize_html, save_file, delete_file


def create_app(config_name=None):
    if config_name is None:
        config_name = os.environ.get('FLASK_CONFIG', 'default')

    app = Flask(__name__)
    app.config.from_object(config[config_name])
    
    app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
        'pool_pre_ping': True,
        'pool_recycle': 25
    }

    # Ensure upload directories exist
    for sub in ['images', 'videos', 'audio', 'avatars', 'logos']:
        os.makedirs(os.path.join(app.config['UPLOAD_FOLDER'], sub), exist_ok=True)

    db.init_app(app)
    Migrate(app, db)
    CSRFProtect(app)

    login_manager = LoginManager()
    login_manager.init_app(app)
    login_manager.login_view = 'admin_login'
    login_manager.login_message_category = 'warning'

    @login_manager.user_loader
    def load_user(user_id):
        return db.session.get(User, int(user_id))

    # ── Context processors ──────────────────────────────────────────────
    @app.context_processor
    def inject_globals():
        lang = session.get('lang', 'en')
        categories = Category.query.filter_by(is_active=True).order_by(Category.order).all()
        breaking = Article.query.filter_by(status='published', is_breaking=True)\
            .order_by(Article.published_at.desc()).first()
        header_logo = SiteSettings.get('header_logo', 'images/logo.jpeg')
        footer_logo = SiteSettings.get('footer_logo', 'images/logo.jpeg')
        header_logo_height = SiteSettings.get('header_logo_height', '80')
        footer_logo_height = SiteSettings.get('footer_logo_height', '300')
        return dict(
            lang=lang,
            is_rtl=lang == 'ar',
            categories=categories,
            breaking_news=breaking,
            site_name='Dabaywa Sudan' if lang == 'en' else 'دبايوا سودان',
            current_year=datetime.utcnow().year,
            header_logo=header_logo,
            footer_logo=footer_logo,
            header_logo_height=header_logo_height,
            footer_logo_height=footer_logo_height,
        )

    # ── Language switch ─────────────────────────────────────────────────
    @app.route('/lang/<lang_code>')
    def set_language(lang_code):
        if lang_code in ('en', 'ar'):
            session['lang'] = lang_code
        return redirect(request.referrer or url_for('index'))

    # ══════════════════════════════════════════════════════════════════════
    #  PUBLIC ROUTES
    # ══════════════════════════════════════════════════════════════════════

    @app.route('/')
    def index():
        lang = session.get('lang', 'en')
        featured = Article.query.filter_by(status='published', is_featured=True)\
            .order_by(Article.published_at.desc()).limit(5).all()
        latest = Article.query.filter_by(status='published')\
            .order_by(Article.published_at.desc()).limit(12).all()
        # Group by category for section display
        cat_articles = {}
        active_cats = Category.query.filter_by(is_active=True).order_by(Category.order).all()
        for cat in active_cats:
            arts = Article.query.filter_by(status='published', category_id=cat.id)\
                .order_by(Article.published_at.desc()).limit(4).all()
            if arts:
                cat_articles[cat] = arts
        most_read = Article.query.filter_by(status='published')\
            .order_by(Article.views.desc()).limit(5).all()
        return render_template('index.html', featured=featured, latest=latest,
                               cat_articles=cat_articles, most_read=most_read, lang=lang)

    @app.route('/article/<slug>')
    def article_detail(slug):
        lang = session.get('lang', 'en')
        article = Article.query.filter_by(slug=slug, status='published').first_or_404()
        article.views += 1
        db.session.commit()
        related = Article.query.filter(
            Article.category_id == article.category_id,
            Article.id != article.id,
            Article.status == 'published'
        ).order_by(Article.published_at.desc()).limit(4).all()
        most_read = Article.query.filter(
            Article.status == 'published'
        ).order_by(Article.views.desc()).limit(5).all()
        return render_template('article.html', article=article, related=related,
                               most_read=most_read, lang=lang)

    @app.route('/category/<slug>')
    def category_view(slug):
        lang = session.get('lang', 'en')
        category = Category.query.filter_by(slug=slug, is_active=True).first_or_404()
        page = request.args.get('page', 1, type=int)
        articles = Article.query.filter_by(status='published', category_id=category.id)\
            .order_by(Article.published_at.desc())\
            .paginate(page=page, per_page=12, error_out=False)
        return render_template('category.html', category=category,
                               articles=articles, lang=lang)

    @app.route('/tag/<slug>')
    def tag_view(slug):
        lang = session.get('lang', 'en')
        tag = Tag.query.filter_by(slug=slug).first_or_404()
        page = request.args.get('page', 1, type=int)
        articles = tag.articles.filter_by(status='published')\
            .order_by(Article.published_at.desc())\
            .paginate(page=page, per_page=12, error_out=False)
        return render_template('tag.html', tag=tag, articles=articles, lang=lang)

    @app.route('/search')
    def search():
        lang = session.get('lang', 'en')
        q = request.args.get('q', '').strip()
        page = request.args.get('page', 1, type=int)
        if q:
            articles = Article.query.filter(
                Article.status == 'published',
                db.or_(
                    Article.title_en.ilike(f'%{q}%'),
                    Article.title_ar.ilike(f'%{q}%'),
                    Article.content_en.ilike(f'%{q}%'),
                    Article.content_ar.ilike(f'%{q}%'),
                )
            ).order_by(Article.published_at.desc())\
             .paginate(page=page, per_page=12, error_out=False)
        else:
            articles = None
        return render_template('search.html', articles=articles, query=q, lang=lang)

    @app.route('/about')
    def about():
        lang = session.get('lang', 'en')
        return render_template('about.html', lang=lang)

    @app.route('/contact')
    def contact():
        lang = session.get('lang', 'en')
        return render_template('contact.html', lang=lang)

    # ══════════════════════════════════════════════════════════════════════
    #  ADMIN ROUTES
    # ══════════════════════════════════════════════════════════════════════

    def admin_required(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.is_authenticated or not current_user.is_admin:
                abort(403)
            return f(*args, **kwargs)
        return decorated_function

    @app.route('/admin/login', methods=['GET', 'POST'])
    def admin_login():
        if current_user.is_authenticated:
            return redirect(url_for('admin_dashboard'))
        form = LoginForm()
        if form.validate_on_submit():
            user = User.query.filter_by(username=form.username.data).first()
            if user and user.check_password(form.password.data) and user.is_active_user:
                login_user(user, remember=form.remember.data)
                flash('Welcome back!', 'success')
                next_page = request.args.get('next')
                # Prevent open redirect
                if next_page and not next_page.startswith('/'):
                    next_page = None
                return redirect(next_page or url_for('admin_dashboard'))
            flash('Invalid username or password.', 'danger')
        return render_template('admin/login.html', form=form)

    @app.route('/admin/logout')
    @login_required
    def admin_logout():
        logout_user()
        flash('You have been logged out.', 'info')
        return redirect(url_for('admin_login'))

    @app.route('/admin')
    @login_required
    def admin_dashboard():
        total_articles = Article.query.count()
        published = Article.query.filter_by(status='published').count()
        pending = Article.query.filter_by(status='pending').count()
        drafts = Article.query.filter_by(status='draft').count()
        total_users = User.query.count()
        total_categories = Category.query.count()
        recent = Article.query.order_by(Article.created_at.desc()).limit(10).all()
        return render_template('admin/dashboard.html',
                               total_articles=total_articles, published=published,
                               pending=pending, drafts=drafts, total_users=total_users,
                               total_categories=total_categories, recent=recent)

    # ── Article Management ──────────────────────────────────────────────

    @app.route('/admin/articles')
    @login_required
    def admin_articles():
        page = request.args.get('page', 1, type=int)
        status_filter = request.args.get('status', '')
        query = Article.query
        if status_filter:
            query = query.filter_by(status=status_filter)
        if not current_user.is_admin:
            query = query.filter_by(author_id=current_user.id)
        articles = query.order_by(Article.created_at.desc())\
            .paginate(page=page, per_page=20, error_out=False)
        return render_template('admin/articles.html', articles=articles,
                               status_filter=status_filter)

    @app.route('/admin/articles/new', methods=['GET', 'POST'])
    @login_required
    def admin_article_new():
        form = ArticleForm()
        form.category_id.choices = [(c.id, c.name_en) for c in
                                     Category.query.filter_by(is_active=True).order_by(Category.order).all()]
        if form.validate_on_submit():
            # Generate unique slug
            base_slug = slugify(form.title_en.data, max_length=300)
            slug = base_slug
            counter = 1
            while Article.query.filter_by(slug=slug).first():
                slug = f"{base_slug}-{counter}"
                counter += 1

            article = Article(
                title_en=form.title_en.data,
                subtitle_en=form.subtitle_en.data or '',
                content_en=sanitize_html(form.content_en.data),
                title_ar=form.title_ar.data or '',
                subtitle_ar=form.subtitle_ar.data or '',
                content_ar=sanitize_html(form.content_ar.data) if form.content_ar.data else '',
                slug=slug,
                status=form.status.data,
                youtube_url=form.youtube_url.data or '',
                featured_image_caption=form.featured_image_caption.data or '',
                is_featured=form.is_featured.data,
                is_breaking=form.is_breaking.data,
                author_id=current_user.id,
                category_id=form.category_id.data,
            )

            # Handle file uploads
            if form.featured_image.data:
                article.featured_image = save_file(form.featured_image.data, 'images') or ''
            if form.video_file.data:
                article.video_url = save_file(form.video_file.data, 'videos') or ''
            if form.audio_file.data:
                article.audio_url = save_file(form.audio_file.data, 'audio') or ''

            # Handle tags
            if form.tags.data:
                for tag_name in form.tags.data.split(','):
                    tag_name = tag_name.strip()
                    if not tag_name:
                        continue
                    tag_slug = slugify(tag_name)
                    tag = Tag.query.filter_by(slug=tag_slug).first()
                    if not tag:
                        tag = Tag(name_en=tag_name, slug=tag_slug)
                        db.session.add(tag)
                    article.tags.append(tag)

            if article.status == 'pending':
                flash('Article submitted for review.', 'info')
            else:
                flash('Article saved as draft.', 'success')

            db.session.add(article)
            db.session.commit()
            return redirect(url_for('admin_articles'))

        return render_template('admin/article_form.html', form=form, editing=False)

    @app.route('/admin/articles/<int:article_id>/edit', methods=['GET', 'POST'])
    @login_required
    def admin_article_edit(article_id):
        article = Article.query.get_or_404(article_id)
        if not current_user.is_admin and article.author_id != current_user.id:
            abort(403)

        form = ArticleForm(obj=article)
        form.category_id.choices = [(c.id, c.name_en) for c in
                                     Category.query.filter_by(is_active=True).order_by(Category.order).all()]

        if request.method == 'GET':
            form.tags.data = ', '.join([t.name_en for t in article.tags])

        if form.validate_on_submit():
            article.title_en = form.title_en.data
            article.subtitle_en = form.subtitle_en.data or ''
            article.content_en = sanitize_html(form.content_en.data)
            article.title_ar = form.title_ar.data or ''
            article.subtitle_ar = form.subtitle_ar.data or ''
            article.content_ar = sanitize_html(form.content_ar.data) if form.content_ar.data else ''
            article.category_id = form.category_id.data
            article.youtube_url = form.youtube_url.data or ''
            article.featured_image_caption = form.featured_image_caption.data or ''
            article.is_featured = form.is_featured.data
            article.is_breaking = form.is_breaking.data
            article.status = form.status.data

            if form.featured_image.data:
                if article.featured_image:
                    delete_file(article.featured_image)
                article.featured_image = save_file(form.featured_image.data, 'images') or ''
            if form.video_file.data:
                if article.video_url:
                    delete_file(article.video_url)
                article.video_url = save_file(form.video_file.data, 'videos') or ''
            if form.audio_file.data:
                if article.audio_url:
                    delete_file(article.audio_url)
                article.audio_url = save_file(form.audio_file.data, 'audio') or ''

            # Update tags
            article.tags.clear()
            if form.tags.data:
                for tag_name in form.tags.data.split(','):
                    tag_name = tag_name.strip()
                    if not tag_name:
                        continue
                    tag_slug = slugify(tag_name)
                    tag = Tag.query.filter_by(slug=tag_slug).first()
                    if not tag:
                        tag = Tag(name_en=tag_name, slug=tag_slug)
                        db.session.add(tag)
                    article.tags.append(tag)

            db.session.commit()
            flash('Article updated.', 'success')
            return redirect(url_for('admin_articles'))

        return render_template('admin/article_form.html', form=form,
                               article=article, editing=True)

    @app.route('/admin/articles/<int:article_id>/approve', methods=['POST'])
    @login_required
    @admin_required
    def admin_article_approve(article_id):
        article = Article.query.get_or_404(article_id)
        article.status = 'published'
        article.published_at = datetime.utcnow()
        db.session.commit()
        flash('Article published!', 'success')
        return redirect(url_for('admin_articles'))

    @app.route('/admin/articles/<int:article_id>/reject', methods=['POST'])
    @login_required
    @admin_required
    def admin_article_reject(article_id):
        article = Article.query.get_or_404(article_id)
        article.status = 'rejected'
        db.session.commit()
        flash('Article rejected.', 'warning')
        return redirect(url_for('admin_articles'))

    @app.route('/admin/articles/<int:article_id>/delete', methods=['POST'])
    @login_required
    @admin_required
    def admin_article_delete(article_id):
        article = Article.query.get_or_404(article_id)
        # Clean up uploaded files
        delete_file(article.featured_image)
        delete_file(article.video_url)
        delete_file(article.audio_url)
        db.session.delete(article)
        db.session.commit()
        flash('Article deleted.', 'success')
        return redirect(url_for('admin_articles'))

    # ── Category Management ─────────────────────────────────────────────

    @app.route('/admin/categories')
    @login_required
    @admin_required
    def admin_categories():
        categories = Category.query.order_by(Category.order).all()
        return render_template('admin/categories.html', categories=categories)

    @app.route('/admin/categories/new', methods=['GET', 'POST'])
    @login_required
    @admin_required
    def admin_category_new():
        form = CategoryForm()
        if form.validate_on_submit():
            cat = Category(
                name_en=form.name_en.data,
                name_ar=form.name_ar.data,
                slug=slugify(form.name_en.data),
                description_en=form.description_en.data or '',
                description_ar=form.description_ar.data or '',
                color=form.color.data or '#c70000',
                order=int(form.order.data or 0),
                is_active=form.is_active.data,
            )
            db.session.add(cat)
            db.session.commit()
            flash('Category created.', 'success')
            return redirect(url_for('admin_categories'))
        return render_template('admin/category_form.html', form=form, editing=False)

    @app.route('/admin/categories/<int:cat_id>/edit', methods=['GET', 'POST'])
    @login_required
    @admin_required
    def admin_category_edit(cat_id):
        cat = Category.query.get_or_404(cat_id)
        form = CategoryForm(obj=cat)
        if form.validate_on_submit():
            cat.name_en = form.name_en.data
            cat.name_ar = form.name_ar.data
            cat.slug = slugify(form.name_en.data)
            cat.description_en = form.description_en.data or ''
            cat.description_ar = form.description_ar.data or ''
            cat.color = form.color.data or '#c70000'
            cat.order = int(form.order.data or 0)
            cat.is_active = form.is_active.data
            db.session.commit()
            flash('Category updated.', 'success')
            return redirect(url_for('admin_categories'))
        return render_template('admin/category_form.html', form=form,
                               category=cat, editing=True)

    @app.route('/admin/categories/<int:cat_id>/delete', methods=['POST'])
    @login_required
    @admin_required
    def admin_category_delete(cat_id):
        cat = Category.query.get_or_404(cat_id)
        if cat.articles.count() > 0:
            flash('Cannot delete category with articles.', 'danger')
            return redirect(url_for('admin_categories'))
        db.session.delete(cat)
        db.session.commit()
        flash('Category deleted.', 'success')
        return redirect(url_for('admin_categories'))

    # ── User Management ─────────────────────────────────────────────────

    @app.route('/admin/users')
    @login_required
    @admin_required
    def admin_users():
        users = User.query.order_by(User.created_at.desc()).all()
        return render_template('admin/users.html', users=users)

    @app.route('/admin/users/new', methods=['GET', 'POST'])
    @login_required
    @admin_required
    def admin_user_new():
        form = UserForm()
        if form.validate_on_submit():
            user = User(
                username=form.username.data,
                email=form.email.data,
                display_name=form.display_name.data,
                display_name_ar=form.display_name_ar.data or '',
                role=form.role.data,
                is_active_user=form.is_active_user.data,
            )
            if form.password.data:
                user.set_password(form.password.data)
            else:
                flash('Password is required for new users.', 'danger')
                return render_template('admin/user_form.html', form=form, editing=False)
            if form.avatar.data:
                user.avatar = save_file(form.avatar.data, 'avatars') or ''
            db.session.add(user)
            db.session.commit()
            flash('User created.', 'success')
            return redirect(url_for('admin_users'))
        return render_template('admin/user_form.html', form=form, editing=False)

    @app.route('/admin/users/<int:user_id>/edit', methods=['GET', 'POST'])
    @login_required
    @admin_required
    def admin_user_edit(user_id):
        user = User.query.get_or_404(user_id)
        form = UserForm(obj=user, original_username=user.username,
                        original_email=user.email)
        if form.validate_on_submit():
            user.username = form.username.data
            user.email = form.email.data
            user.display_name = form.display_name.data
            user.display_name_ar = form.display_name_ar.data or ''
            user.role = form.role.data
            user.is_active_user = form.is_active_user.data
            if form.password.data:
                user.set_password(form.password.data)
            if form.avatar.data:
                if user.avatar:
                    delete_file(user.avatar)
                user.avatar = save_file(form.avatar.data, 'avatars') or ''
            db.session.commit()
            flash('User updated.', 'success')
            return redirect(url_for('admin_users'))
        return render_template('admin/user_form.html', form=form,
                               user=user, editing=True)

    # ── Site Settings ───────────────────────────────────────────────────

    @app.route('/admin/settings', methods=['GET', 'POST'])
    @login_required
    @admin_required
    def admin_settings():
        form = SiteSettingsForm()

        if request.method == 'GET':
            form.header_logo_height.data = SiteSettings.get('header_logo_height', '80')
            form.footer_logo_height.data = SiteSettings.get('footer_logo_height', '300')

        if form.validate_on_submit():
            if form.header_logo.data:
                path = save_file(form.header_logo.data, 'logos')
                if path:
                    old = SiteSettings.get('header_logo', '')
                    if old and old != 'images/logo.jpeg':
                        delete_file(old)
                    SiteSettings.set('header_logo', path)

            if form.footer_logo.data:
                path = save_file(form.footer_logo.data, 'logos')
                if path:
                    old = SiteSettings.get('footer_logo', '')
                    if old and old != 'images/logo.jpeg':
                        delete_file(old)
                    SiteSettings.set('footer_logo', path)

            if form.header_logo_height.data:
                SiteSettings.set('header_logo_height', form.header_logo_height.data)
            if form.footer_logo_height.data:
                SiteSettings.set('footer_logo_height', form.footer_logo_height.data)

            flash('Settings saved.', 'success')
            return redirect(url_for('admin_settings'))

        current_header_logo = SiteSettings.get('header_logo', 'images/logo.jpeg')
        current_footer_logo = SiteSettings.get('footer_logo', 'images/logo.jpeg')
        return render_template('admin/settings.html', form=form,
                               current_header_logo=current_header_logo,
                               current_footer_logo=current_footer_logo)

    # ── Image upload for rich text editor ───────────────────────────────
    @app.route('/admin/upload-image', methods=['POST'])
    @login_required
    def admin_upload_image():
        if 'file' not in request.files:
            return jsonify({'error': 'No file'}), 400
        f = request.files['file']
        if not f.filename:
            return jsonify({'error': 'No file selected'}), 400
        allowed = current_app.config['ALLOWED_IMAGE_EXTENSIONS']
        ext = f.filename.rsplit('.', 1)[-1].lower() if '.' in f.filename else ''
        if ext not in allowed:
            return jsonify({'error': 'File type not allowed'}), 400
        path = save_file(f, 'images')
        return jsonify({'url': url_for('static', filename=path)})

    # ── Error handlers ──────────────────────────────────────────────────

    @app.errorhandler(404)
    def not_found(e):
        return render_template('errors/404.html'), 404

    @app.errorhandler(403)
    def forbidden(e):
        return render_template('errors/403.html'), 403

    return app


app = create_app()

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        # Create default admin if none exists
        if not User.query.filter_by(role='admin').first():
            admin = User(
                username='admin',
                email='admin@dabaywa.sd',
                display_name='Administrator',
                display_name_ar='المسؤول',
                role='admin',
            )
            admin.set_password('admin123')
            db.session.add(admin)

        # Create default categories if none exist
        if not Category.query.first():
            defaults = [
                ('Red Sea', 'البحر الأحمر', '#c70000', 1),
                ('Kassala', 'كسلا', '#0077b6', 2),
                ('Gedaref', 'القضارف', '#2a7a2a', 3),
                ('National News', 'أخبار وطنية', '#7d0068', 4),
                ('Politics', 'سياسة', '#052962', 5),
                ('Economy', 'اقتصاد', '#bb3b0e', 6),
                ('Sports', 'رياضة', '#0084c6', 7),
                ('Culture', 'ثقافة', '#a1845c', 8),
                ('Opinion', 'رأي', '#ff5943', 9),
            ]
            for name_en, name_ar, color, order in defaults:
                cat = Category(
                    name_en=name_en,
                    name_ar=name_ar,
                    slug=slugify(name_en),
                    color=color,
                    order=order,
                    is_active=True,
                )
                db.session.add(cat)

        db.session.commit()

    app.run(debug=True)