import logging
from datetime import datetime

import pytz

from core.constants import HTTP_HEADERS
from core.url_data_cache import URLDataCache
from data.sig_ref import SIGRef
from data.spot import Spot
from providers.spot.http_spot_provider import HTTPSpotProvider


class GMA(HTTPSpotProvider):
    """Spot provider for General Mountain Activity"""

    POLL_INTERVAL_SEC = 120
    SPOTS_URL = "https://www.gma.rocks/api/spots/25/"
    # GMA spots don't contain the details of the programme they are for, we need a separate lookup for that
    REF_INFO_URL_ROOT = "https://www.gma.rocks/api/ref/?"

    def __init__(self, provider_config):
        # Ensure there is an API key in our config, and set up the query URL using it. If no key is provided,
        # disable this spot provider.
        self._api_key = provider_config.get("api_key", "")
        if self._api_key == "":
            provider_config["enabled"] = False
            logging.warning("GMA spot provider configured but no api key was provided, this API will not be queried.")
        self._url_data_cache = URLDataCache("GMA")

        super().__init__("GMA", provider_config, self.SPOTS_URL + "?key=" + self._api_key, self.POLL_INTERVAL_SEC)

    def _http_response_to_spots(self, http_response):
        new_spots = []
        # Iterate through source data
        if "RCD" in http_response.json():
            for source_spot in http_response.json()["RCD"]:
                # Convert to our spot format
                # Seen GMA spots with no (or empty) lat/lon
                lat = float(source_spot["LAT"]) if (source_spot["LAT"] and source_spot["LAT"] != "") else None
                lon = float(source_spot["LON"]) if (source_spot["LON"] and source_spot["LON"] != "") else None

                # Seen some real janky times from GMA, if we don't understand it just ignore this spot
                try:
                    time = datetime.strptime(source_spot["DATE"] + source_spot["TIME"], "%Y%m%d%H%M").replace(
                                tzinfo=pytz.UTC).timestamp()
                except ValueError:
                    continue

                spot = Spot(source=self.name,
                            dx_call=source_spot["ACTIVATOR"].upper(),
                            de_call=source_spot["SPOTTER"].upper(),
                            # Seen GMA spots with no frequency or with "QRT" in this field
                            freq=float(source_spot["QRG"]) * 1000 if (
                                    source_spot["QRG"] != "" and source_spot["QRG"] != "QRT") else None,
                            # Filter out some weird mode strings
                            mode=source_spot["MODE"].upper() if "<>" not in source_spot["MODE"] else None,
                            comment=source_spot["TEXT"],
                            sig_refs=[SIGRef(id=source_spot["REF"], sig="", name=source_spot["NAME"], latitude=lat,
                                             longitude=lon)],
                            time=time,
                            dx_latitude=lat,
                            dx_longitude=lon,
                            qrt=source_spot["QRG"] == "QRT")

                # GMA doesn't give what programme (SIG) the reference is for until we separately look it up.
                if "REF" in source_spot:
                    try:
                        ref_response = self._url_data_cache.get(self.REF_INFO_URL_ROOT + source_spot["REF"],
                                                          headers=HTTP_HEADERS)
                        # Sometimes this is blank even if it's a 200 response, so handle that
                        if ref_response.ok and ref_response.text is not None and ref_response.text != "" and ref_response.text != "\n":
                            ref_info = ref_response.json()
                            # If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. POTA and WWFF
                            # spots come through with reftype=POTA or reftype=WWFF. SOTA is harder to figure out because both SOTA
                            # and GMA summits come through with reftype=Summit, so we must check for the presence of a "sota" entry
                            # to determine if it's a SOTA summit.
                            if spot.sig_refs and "reftype" in ref_info and ref_info["reftype"] not in ["POTA",
                                                                                                       "WWFF"] and (
                                    ref_info["reftype"] != "Summit" or "sota" not in ref_info or ref_info[
                                "sota"] == ""):
                                match ref_info["reftype"]:
                                    case "Summit":
                                        spot.sig_refs[0].sig = "GMA"
                                        spot.sig = "GMA"
                                    case "IOTA Island":
                                        spot.sig_refs[0].sig = "IOTA"
                                        spot.sig = "IOTA"
                                    case "Lighthouse (ILLW)":
                                        spot.sig_refs[0].sig = "ILLW"
                                        spot.sig = "ILLW"
                                    case "Lighthouse (ARLHS)":
                                        spot.sig_refs[0].sig = "ARLHS"
                                        spot.sig = "ARLHS"
                                    case "Castle":
                                        spot.sig_refs[0].sig = "WCA"
                                        spot.sig = "WCA"
                                    case "Mill":
                                        spot.sig_refs[0].sig = "MOTA"
                                        spot.sig = "MOTA"
                                    case _:
                                        logging.warning("GMA spot found with ref type " + ref_info[
                                            "reftype"] + ", developer needs to add support for this!")
                                        spot.sig_refs[0].sig = ref_info["reftype"]
                                        spot.sig = ref_info["reftype"]

                                # Add to our list. Don't worry about de-duping, removing old spots etc. at this point;
                                # other code will do that for us.
                                new_spots.append(spot)

                        elif not ref_response.from_cache:
                            if not ref_response.ok:
                                logging.warning(
                                    f"HTTP {ref_response.status_code} when looking up GMA ref {source_spot['REF']}")
                            else:
                                logging.warning(
                                    f"GMA API returned a malformed response when looking up ref {source_spot['REF']}")
                    except:
                        logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[
                            "REF"] + ", ignoring this spot for now", exc_info=True)
        else:
            logging.warning(f"The GMA API returned an unexpected response (HTTP {http_response.status_code}).")

        return new_spots

    def can_submit_spot(self, sig):
        return sig == "GMA"

    def submit_spot(self, spot, credentials):
        # TODO: Implement.
        # Spotting to GMA is documented: https://www.cqgma.org/api/doc/apigma_spot.pdf We (or the user) need a GMA account, and to send the password in plaintext(!!)
        raise NotImplementedError("GMA upstream spot submission is not yet implemented")
