# ContentDB
# Copyright (C) 2018-23 rubenwardy
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

from celery import uuid
from flask import redirect, url_for, abort, render_template, flash
from flask_babel import gettext
from flask_login import current_user, login_required
from sqlalchemy import or_, and_

from app.models import User, Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \
	PackageRelease, Permission, NotificationType, AuditSeverity, UserRank, PackageType
from app.tasks.importtasks import make_vcs_release
from app.utils import add_notification, add_audit_log
from . import bp


@bp.route("/user/tags/")
def tags_user():
	return redirect(url_for('todo.tags', author=current_user.username))


@bp.route("/user/todo/")
@bp.route("/users/<username>/todo/")
@login_required
def view_user(username=None):
	if username is None:
		return redirect(url_for("todo.view_user", username=current_user.username))

	user: User = User.query.filter_by(username=username).first()
	if not user:
		abort(404)

	if current_user != user and not current_user.rank.at_least(UserRank.APPROVER):
		abort(403)

	unapproved_packages = user.packages \
			.filter(or_(Package.state == PackageState.WIP,
				Package.state == PackageState.CHANGES_NEEDED)) \
			.order_by(db.asc(Package.created_at)).all()

	outdated_packages = user.maintained_packages \
			.filter(Package.state != PackageState.DELETED,
					Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \
			.order_by(db.asc(Package.title)).all()

	missing_game_support = user.maintained_packages.filter(
			Package.state != PackageState.DELETED,
			Package.type.in_([PackageType.MOD, PackageType.TXP]),
			~Package.supported_games.any(),
			Package.supports_all_games == False) \
			.order_by(db.asc(Package.title)).all()

	packages_with_no_screenshots = user.maintained_packages.filter(
			~Package.screenshots.any(), Package.state == PackageState.APPROVED).all()

	packages_with_small_screenshots = user.maintained_packages \
			.filter(Package.state != PackageState.DELETED,
					Package.screenshots.any(and_(PackageScreenshot.width < PackageScreenshot.SOFT_MIN_SIZE[0],
					PackageScreenshot.height < PackageScreenshot.SOFT_MIN_SIZE[1]))) \
			.all()

	topics_to_add = ForumTopic.query \
			.filter_by(author_id=user.id) \
			.filter(~ db.exists().where(Package.forums == ForumTopic.topic_id)) \
			.order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \
			.all()

	needs_tags = user.maintained_packages \
		.filter(Package.state != PackageState.DELETED, ~Package.tags.any()) \
		.order_by(db.asc(Package.title)).all()

	return render_template("todo/user.html", current_tab="user", user=user,
			unapproved_packages=unapproved_packages, outdated_packages=outdated_packages,
			missing_game_support=missing_game_support, needs_tags=needs_tags, topics_to_add=topics_to_add,
			packages_with_no_screenshots=packages_with_no_screenshots,
			packages_with_small_screenshots=packages_with_small_screenshots,
			screenshot_min_size=PackageScreenshot.HARD_MIN_SIZE, screenshot_rec_size=PackageScreenshot.SOFT_MIN_SIZE)


@bp.route("/users/<username>/update-configs/apply-all/", methods=["POST"])
@login_required
def apply_all_updates(username):
	user: User = User.query.filter_by(username=username).first()
	if not user:
		abort(404)

	if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
		abort(403)

	outdated_packages = user.maintained_packages \
		.filter(Package.state != PackageState.DELETED,
			Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \
		.order_by(db.asc(Package.title)).all()

	for package in outdated_packages:
		if not package.check_perm(current_user, Permission.MAKE_RELEASE):
			continue

		if package.releases.filter(or_(PackageRelease.task_id.isnot(None),
				PackageRelease.commit_hash == package.update_config.last_commit)).count() > 0:
			continue

		title = package.update_config.title
		ref = package.update_config.get_ref()

		rel = PackageRelease()
		rel.package = package
		rel.name = title
		rel.title = title
		rel.url = ""
		rel.task_id = uuid()
		db.session.add(rel)
		db.session.commit()

		make_vcs_release.apply_async((rel.id, ref),
									 task_id=rel.task_id)

		msg = "Created release {} (Applied all Git Update Detection)".format(rel.title)
		add_notification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg,
				package.get_url("packages.create_edit"), package)
		add_audit_log(AuditSeverity.NORMAL, current_user, msg, package.get_url("packages.view"), package)
		db.session.commit()

	return redirect(url_for("todo.view_user", username=username))


@bp.route("/user/game_support/")
@bp.route("/users/<username>/game_support/")
@login_required
def all_game_support(username=None):
	if username is None:
		return redirect(url_for("todo.all_game_support", username=current_user.username))

	user: User = User.query.filter_by(username=username).one_or_404()
	if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
		abort(403)

	packages = user.maintained_packages.filter(
				Package.state != PackageState.DELETED,
				Package.type.in_([PackageType.MOD, PackageType.TXP])) \
			.order_by(db.asc(Package.title)).all()

	bulk_support_names = db.session.query(Package.title) \
		.select_from(Package).filter(
			Package.maintainers.contains(user),
			Package.state != PackageState.DELETED,
			Package.type.in_([PackageType.MOD, PackageType.TXP]),
			~Package.supported_games.any(),
			Package.supports_all_games == False) \
		.order_by(db.asc(Package.title)).all()

	bulk_support_names = ", ".join([x[0] for x in bulk_support_names])

	return render_template("todo/game_support.html", user=user, packages=packages, bulk_support_names=bulk_support_names)


@bp.route("/users/<username>/confirm_supports_all_games/", methods=["POST"])
@login_required
def confirm_supports_all_games(username=None):
	user: User = User.query.filter_by(username=username).one_or_404()
	if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
		abort(403)

	packages = user.maintained_packages.filter(
		Package.state != PackageState.DELETED,
		Package.type.in_([PackageType.MOD, PackageType.TXP]),
		~Package.supported_games.any(),
		Package.supports_all_games == False) \
		.all()

	for package in packages:
		package.supports_all_games = True
		db.session.merge(package)

		add_audit_log(AuditSeverity.NORMAL, current_user, "Enabled 'Supports all games' (bulk)",
					  package.get_url("packages.game_support"), package)

	db.session.commit()

	flash(gettext("Done"), "success")
	return redirect(url_for("todo.all_game_support", username=current_user.username))
