from typing import Any, Dict, List, Tuple, Union

from flask import request

from piket_server.aardbei_sync import (
    AARDBEI_ENDPOINT,
    ActivityId,
    get_activity,
    AardbeiLink,
    AardbeiSyncError,
    create_missing,
    get_aardbei_people,
    match_activity,
    get_activities,
    link_matches,
    match_local_aardbei,
    update_names,
)
from piket_server.flask import app, db


def common_prepare_aardbei_sync(
    token: str, endpoint: str
) -> Union[AardbeiSyncError, AardbeiLink]:
    aardbei_people = get_aardbei_people(token, endpoint)

    if isinstance(aardbei_people, AardbeiSyncError):
        return aardbei_people

    aardbei_activities = get_activities(token, endpoint)

    if isinstance(aardbei_activities, AardbeiSyncError):
        return aardbei_activities

    return match_local_aardbei(aardbei_people)


@app.route("/people/aardbei_diff", methods=["POST"])
def aardbei_diff() -> Tuple[Dict[str, Any], int]:
    data: Dict[str, str] = request.json
    link = common_prepare_aardbei_sync(
        data["token"], data.get("endpoint", AARDBEI_ENDPOINT)
    )

    if isinstance(link, AardbeiSyncError):
        return {"error": link.value}, 503

    return (
        {
            "num_changes": link.num_changes,
            "new_people": [member.person.full_name for member in link.remote_only],
            "link_existing": [match.local.name for match in link.matches],
            "altered_name": [match.local.name for match in link.matches],
        },
        200,
    )


@app.route("/people/aardbei_apply", methods=["POST"])
def aardbei_apply() -> Union[Tuple[Dict[str, Any], int]]:
    data: Dict[str, str] = request.json
    link = common_prepare_aardbei_sync(
        data["token"], data.get("endpoint", AARDBEI_ENDPOINT)
    )

    if isinstance(link, AardbeiSyncError):
        return {"error": link.value}, 503

    link_matches(link.matches)
    create_missing(link.remote_only)
    update_names(link.altered_name)

    db.session.commit()

    return (
        {
            "num_changes": link.num_changes,
            "new_people": [member.person.full_name for member in link.remote_only],
            "link_existing": [match.local.name for match in link.matches],
            "altered_name": [match.local.name for match in link.matches],
        },
        200,
    )


@app.route("/aardbei/get_activities", methods=["POST"])
def aardbei_get_activities() -> Tuple[Dict[str, object], int]:
    data: Dict[str, str] = request.json
    activities = get_activities(data["token"], data.get("endpoint", AARDBEI_ENDPOINT))

    if isinstance(activities, AardbeiSyncError):
        return {"error": activities.value}, 503

    return {"activities": [x.as_json_dict for x in activities]}, 200


@app.route("/aardbei/apply_activity", methods=["POST"])
def aardbei_apply_activity() -> Tuple[Dict[str, Any], int]:
    data: Dict[str, Union[str, int]] = request.json
    aid = data["activity_id"]
    token = data["token"]
    endpoint = data["endpoint"]

    if not isinstance(aid, int):
        return {"error": "nonnumeric_activity_id"}, 400

    if not isinstance(token, str):
        return {"error": "illtyped_token"}, 400

    if not isinstance(endpoint, str):
        return {"error": "illtyped_endpoint"}, 400

    activity = get_activity(activity_id=ActivityId(aid), token=token, endpoint=endpoint)

    if isinstance(activity, AardbeiSyncError):
        return {"error": activity.value}, 503

    match_activity(activity)
    db.session.commit()

    return (activity.as_json_dict, 200)