import hashlib
import json
import pyinaturalist as pyinat
from pathlib import Path
from diskcache import Cache
import pyinaturalist as pyinat


path_root = Path(__file__).resolve().parent
cache = Cache(path_root / ".inaturalist_cache")

def make_cache_key(data: dict) -> str:
    serialized = json.dumps(data, sort_keys=True)
    return hashlib.md5(serialized.encode()).hexdigest()


def clear_cache():
    cache.clear()
    print("Cache cleared.")

def get_species_ids(species: list, force_refresh: bool = False):
    cache_key = make_cache_key({
        "function": "get_species_ids",
        "species": species
    })

    if cache_key in cache and not force_refresh:
        return cache[cache_key]

    results = []
    for name in species:
        res = pyinat.get_taxa(q=name, rank="species", per_page=1)
        if res["results"]:
            results.append(res["results"][0]["id"])

    cache[cache_key] = results
    return results

def get_place_id(name: str, force_refresh: bool = False):
    cache_key = f"place_id_{name.lower()}"

    if cache_key in cache and not force_refresh:
        return cache[cache_key]

    res = pyinat.get_places(q=name, per_page=1)

    if res["results"]:
        pid = res["results"][0]["id"]
        cache[cache_key] = pid
        return pid

    cache[cache_key] = None
    return None

def get_country_from_obs(obs):
    place_ids = obs.get("place_ids", [])

    for pid in place_ids:
        cache_key = f"place_full_{pid}"

        if cache_key in cache:
            place = cache[cache_key]
        else:
            res = pyinat.get_places_by_id([pid])
            place = res["results"][0] if res["results"] else None
            cache[cache_key] = place

        # place_type 12 = country
        if place and place.get("place_type") == 12:
            return place.get("name")

    return None

def get_observations_by_id(
    taxon_ids: list,
    country: str = None,
    year_range: tuple = None,
    lat_min: float = None,
    lat_max: float = None,
    lon_min: float = None,
    lon_max: float = None,
    per_page: int = 200,
    force_refresh: bool = False,
    cache_expire: int = None
):

    query_signature = {
        "taxon_ids": taxon_ids,
        "country": country,
        "year_range": year_range,
        "lat_min": lat_min,
        "lat_max": lat_max,
        "lon_min": lon_min,
        "lon_max": lon_max,
        "per_page": per_page
    }

    cache_key = make_cache_key(query_signature)

    if cache_key in cache and not force_refresh:
        print("Returning iNaturalist data from cache...")
        return cache[cache_key]

    print("Downloading observations from iNaturalist...")

    all_results = []

    for taxon_id in taxon_ids:
        params = {
            "taxon_id": taxon_id,
            "per_page": per_page,
            "geo": True,
            "page": "all"
        }

        # Country filter
        if country:
            place_id = get_place_id(country)
            if place_id:
                params["place_id"] = place_id

        # Date filter
        if year_range:
            params["d1"] = f"{year_range[0]}-01-01"
            params["d2"] = f"{year_range[1]}-12-31"

        # Bounding box
        if None not in (lat_min, lat_max, lon_min, lon_max):
            params.update({
                "swlat": lat_min,
                "swlng": lon_min,
                "nelat": lat_max,
                "nelng": lon_max
            })

        res = pyinat.get_observations(**params)

        for obs in res["results"]:
            if not obs:
                continue

            date = obs.get("observed_on_details") or obs.get("created_at_details", {})

            all_results.append({
                "source": "inaturalist",
                "scientificName": obs.get("taxon", {}).get("name"),
                "country": get_country_from_obs(obs),
                "latitude": obs.get("geojson", {}).get("coordinates", [None, None])[1],
                "longitude": obs.get("geojson", {}).get("coordinates", [None, None])[0],
                "day": date.get("day"),
                "month": date.get("month"),
                "year": date.get("year")
            })

    if cache_expire:
        cache.set(cache_key, all_results, expire=cache_expire)
    else:
        cache[cache_key] = all_results

    return all_results