import common
import time
import os
import config
import email_send
import shutil
from celery import shared_task
from app import db
from smtplib import SMTP
from celery.utils.log import get_task_logger


@shared_task(ignore_result=False)
def send_notification(user_notification_id):
    from models import UserNotification
    user_notification = db.session.get(UserNotification, user_notification_id)
    user = user_notification.user
    notification = user_notification.notification

    if user.email:
        with (SMTP(config.MAIL_SERVER) as mail):
            if notification.data.get("type") == "welcome":
                message = email_send.make_multipart_message(
                        f"Welcome, {user.username}",
                        config.NOTIFICATION_EMAIL,
                        user.email,
                        "welcome",
                        username=user.username)

            mail.sendmail(config.NOTIFICATION_EMAIL, user.email, message)

    return 0  # notification sent successfully


@shared_task(ignore_result=False)
def merge_heads(head_route, head_branch, base_route, base_branch, simulate=True):
    from models import Repo
    server_repo_location = os.path.join(config.REPOS_PATH, base_route.lstrip("/"))
    if not os.path.isdir(server_repo_location):
        raise FileNotFoundError(f"Repo {server_repo_location} not found, cannot merge.")

    if base_route == head_route:
        common.git_command(server_repo_location, b"", "checkout", f"{base_branch}")
        if simulate:
            out, err, merge_exit = common.git_command(server_repo_location, b"", "merge", "--no-commit", "--no-ff", f"heads/{head_branch}",
                                                      return_err=True, return_exit=True)

            # Undo the merge.
            common.git_command(server_repo_location, b"", "merge", "--abort")
        else:
            out, err, merge_exit = common.git_command(server_repo_location, b"", "merge", f"heads/{head_branch}",
                                                      return_err=True, return_exit=True)

        return "merge_simulator" if simulate else "merge", out, err, head_route, head_branch, base_route, base_branch, merge_exit

    # Otherwise, we need to fetch the head repo.
    remote_url = os.path.join(config.BASE_DOMAIN, "git", base_route.lstrip("/"))

    out, err = b"", b""
    part_out, part_err = common.git_command(server_repo_location, b"", "remote", "add", "NEW", remote_url, return_err=True)
    out += part_out
    err += part_err
    part_out, part_err = common.git_command(server_repo_location, b"", "remote", "update", return_err=True)
    out += part_out
    err += part_err
    part_out, part_err = common.git_command(server_repo_location, b"", "checkout", f"{base_branch}", return_err=True)
    out += part_out
    err += part_err
    if simulate:
        part_out, part_err, merge_exit = common.git_command(server_repo_location, b"", "merge", "--allow-unrelated-histories",
                                                            "--no-commit", "--no-ff", f"NEW/{head_branch}", return_err=True, return_exit=True)
    else:
        part_out, part_err, merge_exit = common.git_command(server_repo_location, b"", "merge", "--allow-unrelated-histories",
                                                            f"NEW/{head_branch}", return_err=True, return_exit=True)

    diff, diff_exit = common.git_command(server_repo_location, b"", "diff", "--check", return_exit=True)

    out += part_out
    err += part_err
    part_out, part_err = common.git_command(server_repo_location, b"", "remote", "rm", "NEW", return_err=True)
    out += part_out
    err += part_err
    if simulate:
        # Undo the merge.
        common.git_command(server_repo_location, b"", "merge", "--abort")

    return "merge_simulator" if simulate else "merge", out, err, head_route, head_branch, base_route, base_branch, merge_exit


@shared_task(ignore_result=False)
def copy_site(route):
    from models import Repo
    repo = db.session.get(Repo, route)
    server_repo_location = os.path.join(config.REPOS_PATH, route.lstrip("/"))
    subdomain = repo.owner.username
    subpath = repo.name if repo.has_site != 2 else ""
    site_location = os.path.join(config.SITE_PATH, subdomain, subpath)
    # Get the branch to be used for the site; if it somehow doesn't exist, use the default branch.
    branch = repo.site_branch or repo.default_branch
    # Make a shallow clone of the repo; this prevents getting the full git database when it's not needed.
    if os.path.isdir(site_location):
        # Delete the old site.
        shutil.rmtree(site_location)

    common.git_command(config.SITE_PATH, b"", "clone", "--depth=1", f"--branch={branch}", os.path.join(os.getcwd(), server_repo_location), os.path.join(subdomain, subpath))


@shared_task(ignore_result=False)
def delete_site(route):
    from models import Repo
    repo = db.session.get(Repo, route)
    subdomain = repo.owner.username
    subpath = repo.name if repo.has_site != 2 else "."
    site_location = os.path.join(config.SITE_PATH, subdomain, subpath)
    if os.path.isdir(site_location):
        shutil.rmtree(site_location)

    # Redo the primary site.
    primary_site = Repo.query.filter_by(owner=repo.owner, has_site=2).first()
    if primary_site:
        copy_site(primary_site.route)
