from __future__ import annotations
_N='auto_deploy'
_M='/deploy/new'
_L='requirements.txt'
_K='app:app'
_J='/login'
_I='login.html'
_H='admin.login'
_G='failed'
_F='logged_in'
_E='username'
_D='admin.dashboard'
_C='success'
_B='admin.deploy_detail'
_A='error'
from functools import wraps
from flask import Blueprint,flash,redirect,render_template,request,session,url_for
from admin import config
from admin.models import db
from admin.services import deploy as deploy_svc
from admin.services import license as license_svc
from admin.services.util import slugify
bp=Blueprint('admin',__name__)
def current_actor():return(session.get(_E)or config.ADMIN_USERNAME or'admin').strip()
def login_required(view):
	@wraps(view)
	def A(*A,**B):
		if not session.get(_F):return redirect(url_for(_H,next=request.path))
		return view(*A,**B)
	return A
@bp.get(_J)
def login():
	if session.get(_F):return redirect(url_for(_D))
	return render_template(_I)
@bp.post(_J)
def login_post():
	A=(request.form.get(_E)or'').strip();B=request.form.get('password')or''
	if A==config.ADMIN_USERNAME and B==config.ADMIN_PASSWORD:session[_F]=True;session[_E]=A;C=request.args.get('next')or url_for(_D);return redirect(C)
	flash('Invalid username or password.',_A);return render_template(_I),401
@bp.post('/logout')
@login_required
def logout():session.clear();return redirect(url_for(_H))
@bp.get('/')
@login_required
def dashboard():A=db.list_deployments();B=license_svc.get_license_status();return render_template('dashboard.html',deployments=A,dry_run=config.DRY_RUN,public_base=config.PUBLIC_BASE_URL,auto_deploy_global=config.AUTO_DEPLOY_ENABLED,license=B)
@bp.get('/license')
@login_required
def license_page():return render_template('license.html',license=license_svc.get_license_status(),contact_email=config.LICENSE_CONTACT_EMAIL,free_limit=config.FREE_PROJECT_LIMIT)
@bp.post('/license/activate')
@login_required
def license_activate():
	A=(request.form.get('spc_code')or'').strip()
	try:B=license_svc.activate_spc(A,actor=current_actor());flash(B.message,_C)
	except ValueError as C:flash(str(C),_A)
	return redirect(url_for('admin.license_page'))
@bp.get('/events')
@login_required
def events():A=(request.args.get('slug')or'').strip()or None;B=db.list_events(slug=A,limit=150);C=db.list_deployments();return render_template('events.html',events=B,deployments=C,filter_slug=A)
@bp.get(_M)
@login_required
def deploy_new():
	A=license_svc.get_license_status()
	if not A.can_add_project:return render_template('license_gate.html',license=A,contact_email=config.LICENSE_CONTACT_EMAIL,free_limit=config.FREE_PROJECT_LIMIT)
	return render_template('deploy_new.html',default_entrypoint=_K,default_requirements=_L,license=A)
@bp.post(_M)
@login_required
def deploy_new_post():
	E='admin.deploy_new';B=(request.form.get('name')or'').strip();C=(request.form.get('slug')or'').strip()or slugify(B);D=(request.form.get('source_path')or'').strip();F=(request.form.get('entrypoint')or _K).strip();G=(request.form.get('requirements_path')or _L).strip();H=request.form.get(_N)=='on'
	try:
		if not D:flash('Project folder path is required.',_A);return redirect(url_for(E))
		A=deploy_svc.create_and_deploy_folder(name=B or C,slug=C,source_path=D,entrypoint=F,requirements_path=G,auto_deploy=H,actor=current_actor())
	except deploy_svc.DeployError as I:flash(str(I),_A);return redirect(url_for(E))
	if A.status==_G:flash(A.last_error or'Deploy failed. See log.',_A)
	else:flash(f"Deployed {A.slug} → {A.public_url}",_C)
	return redirect(url_for(_B,slug=A.slug))
@bp.get('/deploy/<slug>')
@login_required
def deploy_detail(slug):
	A=slug;B=db.get_by_slug(A)
	if not B:flash('Deployment not found.',_A);return redirect(url_for(_D))
	C=deploy_svc.read_log(A);D=db.list_events(slug=A,limit=40);E=db.list_releases(A,limit=20);return render_template('deploy_detail.html',deployment=B,log_text=C,dry_run=config.DRY_RUN,events=D,releases=E,watch_interval=config.AUTO_DEPLOY_INTERVAL_SEC)
@bp.post('/deploy/<slug>/redeploy')
@login_required
def redeploy(slug):
	A=slug
	try:B=deploy_svc.redeploy(A,actor=current_actor())
	except deploy_svc.DeployError as C:flash(str(C),_A);return redirect(url_for(_B,slug=A))
	if B.status==_G:flash(B.last_error or'Redeploy failed.',_A)
	else:flash('Redeploy complete — synced from connected folder.',_C)
	return redirect(url_for(_B,slug=A))
@bp.post('/deploy/<slug>/rollback/<int:release_id>')
@login_required
def rollback(slug,release_id):
	A=slug
	try:B=deploy_svc.rollback(A,release_id,actor=current_actor())
	except deploy_svc.DeployError as C:flash(str(C),_A);return redirect(url_for(_B,slug=A))
	if B.status==_G:flash(B.last_error or'Rollback failed.',_A)
	else:flash('Rollback complete.',_C)
	return redirect(url_for(_B,slug=A))
@bp.post('/deploy/<slug>/auto-deploy')
@login_required
def toggle_auto_deploy(slug):
	A=request.form.get(_N)=='on'
	try:deploy_svc.set_auto_deploy(slug,A,actor=current_actor());flash(f"Auto-deploy {"enabled"if A else"disabled"}.",_C)
	except deploy_svc.DeployError as B:flash(str(B),_A)
	return redirect(url_for(_B,slug=slug))
@bp.post('/deploy/<slug>/stop')
@login_required
def stop(slug):
	try:deploy_svc.stop_app(slug,actor=current_actor());flash('App pool stopped.',_C)
	except deploy_svc.DeployError as A:flash(str(A),_A)
	return redirect(url_for(_B,slug=slug))
@bp.post('/deploy/<slug>/start')
@login_required
def start(slug):
	try:deploy_svc.start_app(slug,actor=current_actor());flash('App pool started.',_C)
	except deploy_svc.DeployError as A:flash(str(A),_A)
	return redirect(url_for(_B,slug=slug))
@bp.post('/deploy/<slug>/delete')
@login_required
def delete(slug):
	A=slug
	try:deploy_svc.delete_app(A,actor=current_actor());flash(f"Deleted {A}.",_C)
	except deploy_svc.DeployError as B:flash(str(B),_A);return redirect(url_for(_B,slug=A))
	return redirect(url_for(_D))
