import json
import logging
from typing import Optional
import urllib

import requests

from thefuzz import fuzz

logger = logging.getLogger(__name__)

ISBN_URL = "https://openlibrary.org/isbn/{isbn}.json"
SEARCH_URL = (
    "https://openlibrary.org/search.json?q={query}&sort=editions&mode=everything"
)
AUTHOR_SEARCH_URL = "https://openlibrary.org/search/authors.json?q={query}"
COVER_URL = "https://covers.openlibrary.org/b/olid/{id}-L.jpg"
AUTHOR_URL = "https://openlibrary.org/authors/{id}.json"
AUTHOR_IMAGE_URL = "https://covers.openlibrary.org/a/olid/{id}-L.jpg"


def get_first(key: str, result: dict) -> str:
    obj = ""
    if obj_list := result.get(key):
        obj = obj_list[0]
    return obj


def get_author_openlibrary_id(name: str) -> str:
    search_url = AUTHOR_SEARCH_URL.format(query=name)
    response = requests.get(search_url)

    if response.status_code != 200:
        logger.warn(f"Bad response from OL: {response.status_code}")
        return ""

    results = json.loads(response.content)

    if not results:
        logger.warn(f"No author results found from search for {name}")
        return ""

    try:
        result = results.get("docs", [])[0]
    except IndexError:
        result = {"key": ""}
    return result.get("key")


def lookup_author_from_openlibrary(olid: str) -> dict:
    author_url = AUTHOR_URL.format(id=olid)
    response = requests.get(author_url)

    if response.status_code != 200:
        logger.warn(f"Bad response from OL: {response.status_code}")
        return {}

    results = json.loads(response.content)

    if not results:
        logger.warn(f"No author results found from OL for {olid}")
        return {}

    remote_ids = results.get("remote_ids", {})
    bio = ""
    if results.get("bio"):
        try:
            bio = results.get("bio").get("value")
        except AttributeError:
            bio = results.get("bio")
    return {
        "name": results.get("name"),
        "openlibrary_id": olid,
        "wikipedia_url": results.get("wikipedia"),
        "wikidata_id": remote_ids.get("wikidata"),
        "isni": remote_ids.get("isni"),
        "goodreads_id": remote_ids.get("goodreads"),
        "librarything_id": remote_ids.get("librarything"),
        "amazon_id": remote_ids.get("amazon"),
        "bio": bio,
        "author_headshot_url": AUTHOR_IMAGE_URL.format(id=olid),
    }


def lookup_book_from_openlibrary(title: str, author: Optional[str] = None) -> dict:
    title_quoted = urllib.parse.quote(title)
    author_quoted = ""
    if author:
        author_quoted = urllib.parse.quote(author)
    query = f"{title_quoted} {author_quoted}"

    search_url = SEARCH_URL.format(query=query)
    response = requests.get(search_url)

    if response.status_code != 200:
        logger.warn(f"Bad response from OL: {response.status_code}")
        return {}

    results = json.loads(response.content)

    if len(results.get("docs")) == 0:
        logger.warn(f"No results found from OL for {title}")
        return {}

    top = None
    for result in results.get("docs"):
        if fuzz.ratio(title.lower(), result.get("title", "").lower()) > 90:
            top = result
            break

    if not top:
        for result in results.get("docs"):
            # These Summary things suck and ruin our one-shot search
            if "Summary of" in result.get("title"):
                continue

            if title.lower() in result.get("title", "").lower():
                top = result

    if not top and len(results.get("docs")) > 0:
        top = results.get("docs")[0]

    if not top:
        logger.warn(f"No book found for query {query}")
        return {}

    ol_id = top.get("cover_edition_key")
    ol_author_id = get_first("author_key", top)
    first_sentence = ""
    if top.get("first_sentence"):
        try:
            first_sentence = top.get("first_sentence")[0].get("value")
        except AttributeError:
            first_sentence = top.get("first_sentence")[0]
    isbn = None
    if top.get("isbn"):
        isbn = top.get("isbn")[0]
    return {
        "title": top.get("title"),
        "isbn": isbn,
        "openlibrary_id": ol_id,
        "goodreads_id": get_first("id_goodreads", top),
        "first_publish_year": top.get("first_publish_year"),
        "first_sentence": first_sentence,
        "pages": top.get("number_of_pages_median", None),
        "cover_url": COVER_URL.format(id=ol_id),
        "ol_author_id": ol_author_id,
        "subject_key_list": top.get("subject_key", []),
    }
