from datetime import datetime
from functools import wraps
from flask import render_template, redirect, url_for, flash, request, abort
from flask_login import login_required, current_user
from app.extensions import db
from app.models import News, NewsCategory, Activity, Announcement, Lecturer, SiteSetting, Menu, ContactMessage, ActivityLog
from app.forms import NewsForm, ActivityForm, SettingForm
from app.services.content import unique_slug, save_image
from app.admin import bp

def roles_required(*roles):
    def decorator(fn):
        @wraps(fn)
        @login_required
        def wrapped(*args, **kwargs):
            if not current_user.has_role(*roles):
                abort(403)
            return fn(*args, **kwargs)
        return wrapped
    return decorator

def log_action(action, entity, entity_id=None):
    db.session.add(ActivityLog(user_id=current_user.id, action=action, entity=entity, entity_id=entity_id, ip_address=request.remote_addr))

@bp.route("/dashboard")
@roles_required("super_admin", "content_admin", "editor", "contributor")
def dashboard():
    stats = {
        "news": News.query.filter(News.deleted_at.is_(None)).count(),
        "activities": Activity.query.count(),
        "announcements": Announcement.query.count(),
        "lecturers": Lecturer.query.count(),
        "messages": ContactMessage.query.count(),
        "drafts": News.query.filter_by(status="draft").count(),
        "scheduled": News.query.filter(News.status == "published", News.published_at > datetime.utcnow()).count(),
    }
    recent_news = News.query.order_by(News.updated_at.desc()).limit(5).all()
    recent_messages = ContactMessage.query.order_by(ContactMessage.created_at.desc()).limit(5).all()
    return render_template("admin/dashboard.html", stats=stats, recent_news=recent_news, recent_messages=recent_messages)

@bp.route("/berita")
@roles_required("super_admin", "content_admin", "editor", "contributor")
def news_index():
    page = request.args.get("page", 1, type=int)
    q = request.args.get("q", "").strip()
    query = News.query.filter(News.deleted_at.is_(None))
    if q: query = query.filter(News.title.ilike(f"%{q}%"))
    pagination = query.order_by(News.updated_at.desc()).paginate(page=page, per_page=12, error_out=False)
    return render_template("admin/news_index.html", pagination=pagination, q=q)

@bp.route("/berita/tambah", methods=["GET","POST"])
@roles_required("super_admin", "content_admin", "editor", "contributor")
def news_create():
    form = NewsForm()
    form.category_id.choices = [(0, "Tanpa kategori")] + [(c.id,c.name) for c in NewsCategory.query.order_by(NewsCategory.name).all()]
    if form.validate_on_submit():
        try:
            image = save_image(form.image.data) if form.image.data else None
            status = form.status.data
            if current_user.has_role("contributor", "editor") and not current_user.has_role("super_admin", "content_admin"):
                status = "pending" if current_user.has_role("editor") and status == "published" else "draft"
            title = form.title.data.strip()
            item = News(title=title, slug=unique_slug(News, form.slug.data.strip() or title), summary=form.summary.data or "", body=form.body.data, image=image, status=status, published_at=datetime.utcnow() if status == "published" else None, author_id=current_user.id, category_id=form.category_id.data or None)
            db.session.add(item); db.session.flush(); log_action("create","news",item.id); db.session.commit()
            flash("Berita berhasil disimpan.", "success")
            return redirect(url_for("admin.news_index"))
        except ValueError as exc:
            flash(str(exc), "danger")
    return render_template("admin/news_form.html", form=form, heading="Tambah berita")

@bp.route("/berita/<int:item_id>/edit", methods=["GET","POST"])
@roles_required("super_admin", "content_admin", "editor", "contributor")
def news_edit(item_id):
    item = News.query.filter_by(id=item_id).filter(News.deleted_at.is_(None)).first_or_404()
    if current_user.has_role("contributor") and not current_user.has_role("super_admin","content_admin") and item.author_id != current_user.id:
        abort(403)
    form = NewsForm(obj=item)
    form.category_id.choices = [(0, "Tanpa kategori")] + [(c.id,c.name) for c in NewsCategory.query.order_by(NewsCategory.name).all()]
    if request.method == "GET": form.category_id.data = item.category_id or 0
    if form.validate_on_submit():
        try:
            item.title = form.title.data.strip()
            item.slug = unique_slug(News, form.slug.data.strip() or item.title, item.id)
            item.summary = form.summary.data or ""
            item.body = form.body.data
            item.category_id = form.category_id.data or None
            status = form.status.data
            if current_user.has_role("contributor", "editor") and not current_user.has_role("super_admin", "content_admin"):
                status = "pending" if current_user.has_role("editor") and status == "published" else "draft"
            item.status = status
            item.published_at = datetime.utcnow() if status == "published" and not item.published_at else item.published_at
            if form.image.data: item.image = save_image(form.image.data)
            log_action("update","news",item.id); db.session.commit()
            flash("Berita berhasil diperbarui.", "success")
            return redirect(url_for("admin.news_index"))
        except ValueError as exc: flash(str(exc), "danger")
    return render_template("admin/news_form.html", form=form, heading="Edit berita")

@bp.route("/berita/<int:item_id>/hapus", methods=["POST"])
@roles_required("super_admin", "content_admin")
def news_delete(item_id):
    item = News.query.get_or_404(item_id)
    item.deleted_at = datetime.utcnow()
    log_action("soft_delete","news",item.id); db.session.commit()
    flash("Berita dipindahkan ke arsip.", "success")
    return redirect(url_for("admin.news_index"))

@bp.route("/aktivitas")
@roles_required("super_admin", "content_admin", "editor", "contributor")
def activities_index():
    pagination = Activity.query.order_by(Activity.created_at.desc()).paginate(page=request.args.get("page",1,type=int), per_page=12, error_out=False)
    return render_template("admin/activities_index.html", pagination=pagination)

@bp.route("/aktivitas/tambah", methods=["GET","POST"])
@roles_required("super_admin", "content_admin", "editor", "contributor")
def activity_create():
    form = ActivityForm()
    if form.validate_on_submit():
        title = form.title.data.strip()
        status = form.status.data if current_user.has_role("super_admin","content_admin") else "draft"
        item = Activity(title=title, slug=unique_slug(Activity, form.slug.data.strip() or title), category=form.category.data.strip(), location=form.location.data, description=form.description.data, status=status)
        db.session.add(item); db.session.flush(); log_action("create","activity",item.id); db.session.commit()
        flash("Aktivitas berhasil disimpan.", "success")
        return redirect(url_for("admin.activities_index"))
    return render_template("admin/activity_form.html", form=form, heading="Tambah aktivitas")

@bp.route("/aktivitas/<int:item_id>/edit", methods=["GET","POST"])
@roles_required("super_admin", "content_admin", "editor", "contributor")
def activity_edit(item_id):
    item = Activity.query.get_or_404(item_id)
    form = ActivityForm(obj=item)
    if form.validate_on_submit():
        item.title=form.title.data.strip(); item.slug=unique_slug(Activity,form.slug.data.strip() or item.title,item.id)
        item.category=form.category.data.strip(); item.location=form.location.data; item.description=form.description.data
        item.status=form.status.data if current_user.has_role("super_admin","content_admin") else "draft"
        log_action("update","activity",item.id); db.session.commit()
        flash("Aktivitas berhasil diperbarui.","success")
        return redirect(url_for("admin.activities_index"))
    return render_template("admin/activity_form.html", form=form, heading="Edit aktivitas")

@bp.route("/aktivitas/<int:item_id>/hapus", methods=["POST"])
@roles_required("super_admin","content_admin")
def activity_delete(item_id):
    item=Activity.query.get_or_404(item_id); db.session.delete(item); log_action("delete","activity",item.id); db.session.commit()
    flash("Aktivitas dihapus.","success"); return redirect(url_for("admin.activities_index"))

@bp.route("/pengaturan", methods=["GET","POST"])
@roles_required("super_admin")
def settings():
    current = {s.key:s.value for s in SiteSetting.query.all()}
    form = SettingForm(data={
        "program_name":current.get("program_name","Pendidikan Teknik Informatika"),
        "tagline":current.get("tagline",""),
        "email":current.get("email",""),
        "address":current.get("address",""),
        "hero_title":current.get("hero_title",""),
        "hero_description":current.get("hero_description","")
    })
    if form.validate_on_submit():
        values = {k: getattr(form,k).data for k in ["program_name","tagline","email","address","hero_title","hero_description"]}
        for key,value in values.items():
            row=SiteSetting.query.filter_by(key=key).first()
            if row: row.value=value or ""
            else: db.session.add(SiteSetting(key=key,value=value or ""))
        log_action("update","site_settings"); db.session.commit()
        flash("Pengaturan website diperbarui.","success"); return redirect(url_for("admin.settings"))
    return render_template("admin/settings.html",form=form)

@bp.route("/menu", methods=["GET","POST"])
@roles_required("super_admin")
def menus():
    if request.method == "POST":
        label=request.form.get("label","").strip(); url=request.form.get("url","").strip()
        location=request.form.get("location","header")
        if label and url.startswith("/") and not url.startswith("/admin") and location in ("header","footer"):
            db.session.add(Menu(label=label,url=url,location=location,sort_order=Menu.query.count()))
            log_action("create","menu"); db.session.commit(); flash("Menu ditambahkan.","success")
        else: flash("Menu tidak valid. Gunakan URL internal yang diawali / dan bukan /admin.","danger")
        return redirect(url_for("admin.menus"))
    items=Menu.query.order_by(Menu.location,Menu.sort_order).all()
    return render_template("admin/menus.html",items=items)

@bp.route("/menu/<int:item_id>/hapus", methods=["POST"])
@roles_required("super_admin")
def menu_delete(item_id):
    item=Menu.query.get_or_404(item_id); db.session.delete(item); log_action("delete","menu",item.id); db.session.commit()
    flash("Menu dihapus.","success"); return redirect(url_for("admin.menus"))

@bp.route("/pesan")
@roles_required("super_admin","content_admin")
def messages():
    items=ContactMessage.query.order_by(ContactMessage.created_at.desc()).paginate(page=request.args.get("page",1,type=int),per_page=20,error_out=False)
    return render_template("admin/messages.html",pagination=items)

@bp.route("/pesan/<int:item_id>/baca", methods=["POST"])
@roles_required("super_admin","content_admin")
def message_read(item_id):
    item=ContactMessage.query.get_or_404(item_id); item.is_read=True; db.session.commit(); flash("Pesan ditandai sudah dibaca.","success"); return redirect(url_for("admin.messages"))

@bp.route("/dosen", methods=["GET","POST"])
@roles_required("super_admin","content_admin")
def lecturers_index():
    if request.method == "POST":
        name=request.form.get("name","").strip()
        if name:
            db.session.add(Lecturer(name=name,title=request.form.get("title","").strip(),expertise=request.form.get("expertise","").strip(),email=request.form.get("email","").strip()))
            db.session.commit(); flash("Dosen ditambahkan.","success")
        return redirect(url_for("admin.lecturers_index"))
    items=Lecturer.query.order_by(Lecturer.name).all()
    return render_template("admin/lecturers_index.html",lecturers=items)

@bp.route("/dosen/<int:item_id>/hapus", methods=["POST"])
@roles_required("super_admin")
def lecturer_delete(item_id):
    item=Lecturer.query.get_or_404(item_id); db.session.delete(item); db.session.commit(); flash("Data dosen dihapus.","success"); return redirect(url_for("admin.lecturers_index"))
