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

from collections import OrderedDict

from .highlighter import highlight_delta
from .checker import do_check, T_POSSIBLE, T_SUSPECT
from .misc import Query, cache
from .sites import update_sites

__all__ = ["format_api_error", "handle_api_request"]

_CHECK_ERRORS = {
    "no search method": "Either 'use_engine' or 'use_links' must be true",
    "bad oldid": "The revision ID is invalid",
    "no URL": "The parameter 'url' is required for URL comparisons",
    "bad URI": "The given URI scheme is unsupported",
    "no data": "No text could be found in the given URL (note that only HTML "
               "and plain text pages are supported, and content generated by "
               "JavaScript or found inside iframes is ignored)",
    "timeout": "The given URL timed out before any data could be retrieved",
    "search error": "An error occurred while using the search engine; try "
                    "reloading or setting 'use_engine' to 0",
}

def _serialize_page(page):
    return OrderedDict((("title", page.title), ("url", page.url)))

def _serialize_source(source, show_skip=True):
    if not source:
        return OrderedDict((
            ("url", None), ("confidence", 0.0), ("violation", "none")))

    conf = source.confidence
    data = OrderedDict((
        ("url", source.url),
        ("confidence", conf),
        ("violation", "suspected" if conf >= T_SUSPECT else
                      "possible" if conf >= T_POSSIBLE else "none")
    ))
    if show_skip:
        data["skipped"] = source.skipped
        data["excluded"] = source.excluded
    return data

def _serialize_detail(result):
    source_chain, delta = result.best.chains
    article = highlight_delta(None, result.article_chain, delta)
    source = highlight_delta(None, source_chain, delta)
    return OrderedDict((("article", article), ("source", source)))

def format_api_error(code, info):
    if isinstance(info, BaseException):
        info = type(info).__name__ + ": " + str(info)
    elif isinstance(info, unicode):
        info = info.encode("utf8")
    error_inner = OrderedDict((("code", code), ("info", info)))
    return OrderedDict((("status", "error"), ("error", error_inner)))

def _hook_default(query):
    info = u"Unknown action: '{0}'".format(query.action.lower())
    return format_api_error("unknown_action", info)

def _hook_check(query):
    do_check(query)
    if not query.submitted:
        info = ("The query parameters 'project', 'lang', and either 'title' "
                "or 'oldid' are required for checks")
        return format_api_error("missing_params", info)
    if query.error:
        info = _CHECK_ERRORS.get(query.error, "An unknown error occurred")
        return format_api_error(query.error.replace(" ", "_"), info)
    elif not query.site:
        info = (u"The given site (project={0}, lang={1}) either doesn't exist,"
                u" is closed, or is private").format(query.project, query.lang)
        return format_api_error("bad_site", info)
    elif not query.result:
        if query.oldid:
            info = u"The revision ID couldn't be found: {0}"
            return format_api_error("bad_oldid", info.format(query.oldid))
        else:
            info = u"The page couldn't be found: {0}"
            return format_api_error("bad_title", info.format(query.page.title))

    result = query.result
    data = OrderedDict((
        ("status", "ok"),
        ("meta", OrderedDict((
            ("time", result.time),
            ("queries", result.queries),
            ("cached", result.cached),
            ("redirected", bool(query.redirected_from))
        ))),
        ("page", _serialize_page(query.page))
    ))
    if result.cached:
        data["meta"]["cache_time"] = result.cache_time
    if query.redirected_from:
        data["original_page"] = _serialize_page(query.redirected_from)
    data["best"] = _serialize_source(result.best, show_skip=False)
    data["sources"] = [_serialize_source(source) for source in result.sources]
    if query.detail in ("1", "true"):
        data["detail"] = _serialize_detail(result)
    return data

def _hook_sites(query):
    update_sites()
    return OrderedDict((
        ("status", "ok"), ("langs", cache.langs), ("projects", cache.projects)
    ))

_HOOKS = {
    "compare": _hook_check,
    "search": _hook_check,
    "sites": _hook_sites,
}

def handle_api_request():
    query = Query()
    if query.version:
        try:
            query.version = int(query.version)
        except ValueError:
            info = "The version string is invalid: {0}".format(query.version)
            return format_api_error("invalid_version", info)
    else:
        query.version = 1

    if query.version == 1:
        action = query.action.lower() if query.action else ""
        return _HOOKS.get(action, _hook_default)(query)

    info = "The API version is unsupported: {0}".format(query.version)
    return format_api_error("unsupported_version", info)

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

from collections import OrderedDict

from .highlighter import highlight_delta
from .checker import do_check, T_POSSIBLE, T_SUSPECT
from .misc import Query, cache
from .sites import update_sites

__all__ = ["format_api_error", "handle_api_request"]

_CHECK_ERRORS = {
    "no search method": "Either 'use_engine' or 'use_links' must be true",
    "bad oldid": "The revision ID is invalid",
    "no URL": "The parameter 'url' is required for URL comparisons",
    "bad URI": "The given URI scheme is unsupported",
    "no data": "No text could be found in the given URL (note that only HTML "
               "and plain text pages are supported, and content generated by "
               "JavaScript or found inside iframes is ignored)",
    "timeout": "The given URL timed out before any data could be retrieved",
    "search error": "An error occurred while using the search engine; try "
                    "reloading or setting 'use_engine' to 0",
}

def _serialize_page(page):
    return OrderedDict((("title", page.title), ("url", page.url)))

def _serialize_source(source, show_skip=True):
    if not source:
        return OrderedDict((
            ("url", None), ("confidence", 0.0), ("violation", "none")))

    conf = source.confidence
    data = OrderedDict((
        ("url", source.url),
        ("confidence", conf),
        ("violation", "suspected" if conf >= T_SUSPECT else
                      "possible" if conf >= T_POSSIBLE else "none")
    ))
    if show_skip:
        data["skipped"] = source.skipped
        data["excluded"] = source.excluded
    return data

def _serialize_detail(result):
    source_chain, delta = result.best.chains
    article = highlight_delta(None, result.article_chain, delta)
    source = highlight_delta(None, source_chain, delta)
    return OrderedDict((("article", article), ("source", source)))

def format_api_error(code, info):
    if isinstance(info, BaseException):
        info = type(info).__name__ + ": " + str(info)
    elif isinstance(info, unicode):
        info = info.encode("utf8")
    error_inner = OrderedDict((("code", code), ("info", info)))
    return OrderedDict((("status", "error"), ("error", error_inner)))

def _hook_default(query):
    info = u"Unknown action: '{0}'".format(query.action.lower())
    return format_api_error("unknown_action", info)

def _hook_check(query):
    do_check(query)
    if not query.submitted:
        info = ("The query parameters 'project', 'lang', and either 'title' "
                "or 'oldid' are required for checks")
        return format_api_error("missing_params", info)
    if query.error:
        info = _CHECK_ERRORS.get(query.error, "An unknown error occurred")
        return format_api_error(query.error.replace(" ", "_"), info)
    elif not query.site:
        info = (u"The given site (project={0}, lang={1}) either doesn't exist,"
                u" is closed, or is private").format(query.project, query.lang)
        return format_api_error("bad_site", info)
    elif not query.result:
        if query.oldid:
            info = u"The revision ID couldn't be found: {0}"
            return format_api_error("bad_oldid", info.format(query.oldid))
        else:
            info = u"The page couldn't be found: {0}"
            return format_api_error("bad_title", info.format(query.page.title))

    result = query.result
    data = OrderedDict((
        ("status", "ok"),
        ("meta", OrderedDict((
            ("time", result.time),
            ("queries", result.queries),
            ("cached", result.cached),
            ("redirected", bool(query.redirected_from))
        ))),
        ("page", _serialize_page(query.page))
    ))
    if result.cached:
        data["meta"]["cache_time"] = result.cache_time
    if query.redirected_from:
        data["original_page"] = _serialize_page(query.redirected_from)
    data["best"] = _serialize_source(result.best, show_skip=False)
    data["sources"] = [_serialize_source(source) for source in result.sources]
    if query.detail in ("1", "true"):
        data["detail"] = _serialize_detail(result)
    return data

def _hook_sites(query):
    update_sites()
    return OrderedDict((
        ("status", "ok"), ("langs", cache.langs), ("projects", cache.projects)
    ))

_HOOKS = {
    "compare": _hook_check,
    "search": _hook_check,
    "sites": _hook_sites,
}

def handle_api_request():
    query = Query()
    if query.version:
        try:
            query.version = int(query.version)
        except ValueError:
            info = "The version string is invalid: {0}".format(query.version)
            return format_api_error("invalid_version", info)
    else:
        query.version = 1

    if query.version == 1:
        action = query.action.lower() if query.action else ""
        return _HOOKS.get(action, _hook_default)(query)

    info = "The API version is unsupported: {0}".format(query.version)
    return format_api_error("unsupported_version", info)

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