# -*- coding: utf-8 -*-

from functools import partial

from flask import (
    Markup,
    Response,
    abort,
    jsonify,
    redirect,
    render_template,
    request,
    stream_with_context,
    url_for,
)
from flask_login import current_user
from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate

from app import (
    current_service,
    format_datetime_short,
    notification_api_client,
    service_api_client,
)
from app.formatters import get_time_left, message_count_noun
from app.main import main
from app.main.forms import SearchNotificationsForm
from app.models.job import Job
from app.utils import parse_filter_args, set_status_filters
from app.utils.csv import generate_notifications_csv
from app.utils.pagination import (
    generate_next_dict,
    generate_previous_dict,
    get_page_from_request,
)
from app.utils.user import user_has_permissions


@main.route("/services/<uuid:service_id>/jobs")
@user_has_permissions()
def view_jobs(service_id):
    return redirect(
        url_for(
            "main.uploads",
            service_id=current_service.id,
        )
    )


@main.route("/services/<uuid:service_id>/jobs/<uuid:job_id>")
@user_has_permissions()
def view_job(service_id, job_id):
    job = Job.from_id(job_id, service_id=current_service.id)
    if job.cancelled:
        abort(404)

    filter_args = parse_filter_args(request.args)
    filter_args["status"] = set_status_filters(filter_args)

    return render_template(
        "views/jobs/job.html",
        job=job,
        status=request.args.get("status", ""),
        updates_url=url_for(
            ".view_job_updates",
            service_id=service_id,
            job_id=job.id,
            status=request.args.get("status", ""),
        ),
        partials=get_job_partials(job),
    )


@main.route("/services/<uuid:service_id>/jobs/<uuid:job_id>.csv")
@user_has_permissions("view_activity")
def view_job_csv(service_id, job_id):
    job = Job.from_id(job_id, service_id=service_id)
    filter_args = parse_filter_args(request.args)
    filter_args["status"] = set_status_filters(filter_args)

    return Response(
        stream_with_context(
            generate_notifications_csv(
                service_id=service_id,
                job_id=job_id,
                status=filter_args.get("status"),
                page=request.args.get("page", 1),
                page_size=5000,
                format_for_csv=True,
                template_type=job.template_type,
            )
        ),
        mimetype="text/csv",
        headers={
            "Content-Disposition": 'inline; filename="{} - {}.csv"'.format(
                job.template["name"], format_datetime_short(job.created_at)
            )
        },
    )


@main.route("/services/<uuid:service_id>/jobs/<uuid:job_id>", methods=["POST"])
@user_has_permissions("send_messages")
def cancel_job(service_id, job_id):
    Job.from_id(job_id, service_id=service_id).cancel()
    return redirect(url_for("main.service_dashboard", service_id=service_id))


@main.route("/services/<uuid:service_id>/jobs/<uuid:job_id>.json")
@user_has_permissions()
def view_job_updates(service_id, job_id):
    job = Job.from_id(job_id, service_id=service_id)

    return jsonify(**get_job_partials(job))


@main.route("/services/<uuid:service_id>/notifications", methods=["GET", "POST"])
@main.route(
    "/services/<uuid:service_id>/notifications/<template_type:message_type>",
    methods=["GET", "POST"],
)
@user_has_permissions()
def view_notifications(service_id, message_type=None):
    return render_template(
        "views/notifications.html",
        partials=get_notifications(service_id, message_type),
        message_type=message_type,
        status=request.args.get("status") or "sending,delivered,failed",
        page=request.args.get("page", 1),
        search_form=SearchNotificationsForm(
            message_type=message_type,
            to=request.form.get("to"),
        ),
        things_you_can_search_by={
            "email": ["email address"],
            "sms": ["phone number"],
            # We say recipient here because combining all 3 types, plus
            # reference gets too long for the hint text
            None: ["recipient"],
        }.get(message_type)
        + {
            True: ["reference"],
            False: [],
        }.get(bool(current_service.api_keys)),
        download_link=url_for(
            ".download_notifications_csv",
            service_id=current_service.id,
            message_type=message_type,
            status=request.args.get("status"),
        ),
    )


@main.route("/services/<uuid:service_id>/notifications.json", methods=["GET", "POST"])
@main.route(
    "/services/<uuid:service_id>/notifications/<template_type:message_type>.json",
    methods=["GET", "POST"],
)
@user_has_permissions()
def get_notifications_as_json(service_id, message_type=None):
    return jsonify(
        get_notifications(
            service_id, message_type, status_override=request.args.get("status")
        )
    )


@main.route(
    "/services/<uuid:service_id>/notifications.csv", endpoint="view_notifications_csv"
)
@main.route(
    "/services/<uuid:service_id>/notifications/<template_type:message_type>.csv",
    endpoint="view_notifications_csv",
)
@user_has_permissions()
def get_notifications(service_id, message_type, status_override=None):  # noqa
    # TODO get the api to return count of pages as well.
    page = get_page_from_request()
    if page is None:
        abort(404, "Invalid page argument ({}).".format(request.args.get("page")))
    filter_args = parse_filter_args(request.args)
    filter_args["status"] = set_status_filters(filter_args)
    service_data_retention_days = None
    search_term = request.form.get("to", "")

    if message_type is not None:
        service_data_retention_days = current_service.get_days_of_retention(
            message_type
        )

    if request.path.endswith("csv") and current_user.has_permissions("view_activity"):
        return Response(
            generate_notifications_csv(
                service_id=service_id,
                page=page,
                page_size=5000,
                template_type=[message_type],
                status=filter_args.get("status"),
                limit_days=service_data_retention_days,
            ),
            mimetype="text/csv",
            headers={"Content-Disposition": 'inline; filename="notifications.csv"'},
        )
    notifications = notification_api_client.get_notifications_for_service(
        service_id=service_id,
        page=page,
        template_type=[message_type] if message_type else [],
        status=filter_args.get("status"),
        limit_days=service_data_retention_days,
        to=search_term,
    )
    url_args = {"message_type": message_type, "status": request.args.get("status")}
    prev_page = None

    if "links" in notifications and notifications["links"].get("prev", None):
        prev_page = generate_previous_dict(
            "main.view_notifications", service_id, page, url_args=url_args
        )
    next_page = None

    if "links" in notifications and notifications["links"].get("next", None):
        next_page = generate_next_dict(
            "main.view_notifications", service_id, page, url_args
        )

    if message_type:
        download_link = url_for(
            ".view_notifications_csv",
            service_id=current_service.id,
            message_type=message_type,
            status=request.args.get("status"),
        )
    else:
        download_link = None

    return {
        "service_data_retention_days": service_data_retention_days,
        "counts": render_template(
            "views/activity/counts.html",
            status=request.args.get("status"),
            status_filters=get_status_filters(
                current_service,
                message_type,
                service_api_client.get_service_statistics(
                    service_id, limit_days=service_data_retention_days
                ),
            ),
        ),
        "notifications": render_template(
            "views/activity/notifications.html",
            notifications=list(
                add_preview_of_content_to_notifications(notifications["notifications"])
            ),
            page=page,
            limit_days=service_data_retention_days,
            prev_page=prev_page,
            next_page=next_page,
            show_pagination=(not search_term),
            status=request.args.get("status"),
            message_type=message_type,
            download_link=download_link,
            single_notification_url=partial(
                url_for,
                ".view_notification",
                service_id=current_service.id,
            ),
        ),
    }


def get_status_filters(service, message_type, statistics):
    if message_type is None:
        stats = {
            key: sum(statistics[message_type][key] for message_type in {"email", "sms"})
            for key in {"requested", "delivered", "failed"}
        }
    else:
        stats = statistics[message_type]
    stats["sending"] = stats["requested"] - stats["delivered"] - stats["failed"]

    filters = [
        # key, label, option
        ("requested", "total", "sending,delivered,failed"),
        ("sending", "pending", "pending"),
        ("delivered", "delivered", "delivered"),
        ("failed", "failed", "failed"),
    ]
    return [
        # return list containing label, option, link, count
        (
            label,
            option,
            url_for(
                ".view_notifications",
                service_id=service.id,
                message_type=message_type,
                status=option,
            ),
            stats[key],
        )
        for key, label, option in filters
    ]


def _get_job_counts(job):
    job_type = job.template_type
    return [
        (
            label,
            query_param,
            url_for(
                ".view_job",
                service_id=job.service,
                job_id=job.id,
                status=query_param,
            ),
            count,
        )
        for label, query_param, count in [
            [
                Markup(
                    f"""total<span class="usa-sr-only">
                    {"text message" if job_type == "sms" else job_type}s</span>"""
                ),
                "",
                job.notification_count,
            ],
            [
                Markup(
                    f"""pending<span class="usa-sr-only">
                    {message_count_noun(job.notifications_sending, job_type)}</span>"""
                ),
                "pending",
                job.notifications_sending,
            ],
            [
                Markup(
                    f"""delivered<span class="usa-sr-only">
                    {message_count_noun(job.notifications_delivered, job_type)}</span>"""
                ),
                "delivered",
                job.notifications_delivered,
            ],
            [
                Markup(
                    f"""failed<span class="usa-sr-only">
                    {message_count_noun(job.notifications_failed, job_type)}</span>"""
                ),
                "failed",
                job.notifications_failed,
            ],
        ]
    ]


def get_job_partials(job):
    filter_args = parse_filter_args(request.args)
    filter_args["status"] = set_status_filters(filter_args)
    notifications = job.get_notifications(status=filter_args["status"])
    counts = render_template(
        "partials/count.html",
        counts=_get_job_counts(job),
        status=filter_args["status"],
        notifications_deleted=(
            job.status == "finished" and not notifications["notifications"]
        ),
    )
    service_data_retention_days = current_service.get_days_of_retention(
        job.template_type
    )

    return {
        "counts": counts,
        "notifications": render_template(
            "partials/jobs/notifications.html",
            notifications=list(
                add_preview_of_content_to_notifications(notifications["notifications"])
            ),
            more_than_one_page=bool(notifications.get("links", {}).get("next")),
            download_link=url_for(
                ".view_job_csv",
                service_id=current_service.id,
                job_id=job.id,
                status=request.args.get("status"),
            ),
            time_left=get_time_left(
                job.created_at, service_data_retention_days=service_data_retention_days
            ),
            job=job,
            service_data_retention_days=service_data_retention_days,
        ),
        "status": render_template(
            "partials/jobs/status.html",
            job=job,
        ),
    }


def add_preview_of_content_to_notifications(notifications):
    for notification in notifications:
        yield (
            dict(
                preview_of_content=get_preview_of_content(notification), **notification
            )
        )


def get_preview_of_content(notification):
    if notification["template"].get("redact_personalisation"):
        notification["personalisation"] = {}

    if notification["template"]["template_type"] == "sms":
        return str(
            SMSBodyPreviewTemplate(
                notification["template"],
                notification["personalisation"],
            )
        )

    if notification["template"]["template_type"] == "email":
        return Markup(
            EmailPreviewTemplate(
                notification["template"],
                notification["personalisation"],
                redact_missing_personalisation=True,
            ).subject
        )
