from sqlalchemy import desc

from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required, login_user, logout_user

from ..extensions import db, limiter
from ..forms import AdminLoginForm, BatchCreateForm, GenerateKeysForm
from ..models import AccessKey, AccessLog, DeliveryBatch, build_dashboard_stats
from ..security import split_nonempty_lines, utcnow_naive
from ..services import add_credentials, issue_keys, log_event

admin_bp = Blueprint("admin", __name__, url_prefix="/admin")


@admin_bp.route("/login", methods=["GET", "POST"])
@limiter.limit("5 per minute")
def login():
    if current_user.is_authenticated:
        return redirect(url_for("admin.dashboard"))

    from ..models import AdminUser

    form = AdminLoginForm()
    if form.validate_on_submit():
        admin = AdminUser.query.filter_by(username=form.username.data.strip()).first()
        if admin and admin.check_password(form.password.data):
            login_user(admin, remember=form.remember.data)
            admin.last_login_at = utcnow_naive()
            db.session.commit()
            next_url = request.args.get("next")
            if next_url and next_url.startswith("/"):
                return redirect(next_url)
            return redirect(url_for("admin.dashboard"))
        flash("Wrong username or password.", "error")

    return render_template("admin/login.html", form=form)


@admin_bp.post("/logout")
@login_required
def logout():
    logout_user()
    flash("You are signed out.", "info")
    return redirect(url_for("admin.login"))


@admin_bp.get("")
@login_required
def dashboard():
    stats = build_dashboard_stats()
    batches = DeliveryBatch.query.order_by(desc(DeliveryBatch.created_at)).limit(6).all()
    logs = AccessLog.query.order_by(desc(AccessLog.created_at)).limit(12).all()
    return render_template("admin/dashboard.html", stats=stats, batches=batches, logs=logs)


@admin_bp.get("/batches")
@login_required
def list_batches():
    batches = DeliveryBatch.query.order_by(desc(DeliveryBatch.created_at)).all()
    return render_template("admin/batches.html", batches=batches)


@admin_bp.route("/batches/new", methods=["GET", "POST"])
@login_required
def new_batch():
    form = BatchCreateForm()
    if form.validate_on_submit():
        raw_credentials = split_nonempty_lines(form.credentials.data or "")
        if not raw_credentials:
            flash("Add at least one credential pair.", "error")
            return render_template("admin/new_batch.html", form=form), 400

        batch = DeliveryBatch(
            name=form.name.data.strip(),
            description=(form.description.data or "").strip() or None,
            created_by=current_user,
        )
        db.session.add(batch)
        db.session.flush()
        add_credentials(batch, raw_credentials)
        generated_keys = issue_keys(
            batch,
            count=form.key_count.data,
            expires_at=form.expires_at.data,
            max_views=form.max_views.data,
            notes=(form.key_notes.data or "").strip() or None,
        )
        log_event("batch_created", batch=batch, detail=f"Created with {len(generated_keys)} key(s)")
        db.session.commit()
        flash("Batch created. Save the generated keys now - they are only shown once.", "success")
        return render_batch_detail(batch, generated_keys=generated_keys)

    return render_template("admin/new_batch.html", form=form)


@admin_bp.get("/batches/<int:batch_id>")
@login_required
def batch_detail(batch_id: int):
    batch = DeliveryBatch.query.get_or_404(batch_id)
    return render_batch_detail(batch)


@admin_bp.post("/batches/<int:batch_id>/generate-keys")
@login_required
def generate_keys(batch_id: int):
    batch = DeliveryBatch.query.get_or_404(batch_id)
    form = GenerateKeysForm(prefix="generate")
    if form.validate_on_submit():
        generated_keys = issue_keys(
            batch,
            count=form.count.data,
            expires_at=form.expires_at.data,
            max_views=form.max_views.data,
            notes=(form.key_notes.data or "").strip() or None,
        )
        log_event("keys_generated", batch=batch, detail=f"Generated {len(generated_keys)} key(s)")
        db.session.commit()
        flash("New keys generated. Save them now - raw values are not stored.", "success")
        return render_batch_detail(batch, generated_keys=generated_keys)

    flash("Fix the extra key form and try again.", "error")
    return render_batch_detail(batch, generate_form=form), 400


@admin_bp.post("/batches/<int:batch_id>/toggle")
@login_required
def toggle_batch(batch_id: int):
    batch = DeliveryBatch.query.get_or_404(batch_id)
    batch.is_active = not batch.is_active
    state = "activated" if batch.is_active else "paused"
    log_event("batch_toggled", batch=batch, detail=state)
    db.session.commit()
    flash(f"Batch {state}.", "success")
    return redirect(url_for("admin.batch_detail", batch_id=batch.id))


@admin_bp.post("/keys/<int:key_id>/revoke")
@login_required
def revoke_key(key_id: int):
    access_key = AccessKey.query.get_or_404(key_id)
    if access_key.revoked_at is None:
        access_key.revoked_at = utcnow_naive()
        access_key.is_active = False
        log_event("key_revoked", batch=access_key.batch, access_key=access_key, detail=access_key.key_hint)
        db.session.commit()
        flash("Key revoked.", "success")
    else:
        flash("That key is already revoked.", "info")
    return redirect(url_for("admin.batch_detail", batch_id=access_key.batch_id))


@admin_bp.get("/logs")
@login_required
def logs():
    entries = AccessLog.query.order_by(desc(AccessLog.created_at)).limit(200).all()
    return render_template("admin/logs.html", logs=entries)


def render_batch_detail(batch: DeliveryBatch, *, generated_keys: list[str] | None = None, generate_form: GenerateKeysForm | None = None):
    if generate_form is None:
        generate_form = GenerateKeysForm(prefix="generate")
    recent_logs = (
        AccessLog.query.filter_by(batch_id=batch.id)
        .order_by(desc(AccessLog.created_at))
        .limit(20)
        .all()
    )
    credentials = [item.value for item in batch.credentials]
    return render_template(
        "admin/batch_detail.html",
        batch=batch,
        generated_keys=generated_keys or [],
        generate_form=generate_form,
        recent_logs=recent_logs,
        credentials=credentials,
    )
