import datetime
import json
import logging
import re

from mercado.core.base import Mercado
from mercado.core.common import nt_merge

log = logging.getLogger(__name__)


class Safeway(Mercado):
    def __init__(self, auth, urls, headers, sleep_multiplier=1.0):
        self.auth = auth
        self.urls = urls
        self.headers = headers
        self.raw_unclipped_list = []
        self.prepared_coupon_list = []
        self.store_id = 0
        self.sleep_multiplier = sleep_multiplier

    def execute(self):
        self._init_session()
        self._login()
        self._get_store_id()
        self._get_unclipped_coupon_list()
        self._get_coupon_details()
        self._post_coupon()

    def _login(self):
        log.info("Logging in as {}".format(self.auth.get("username")))
        login_data = json.dumps(
            {"source": "WEB",
             "rememberMe": False,
             "userId": self.auth.get("username"),
             "password": self.auth.get("password")})
        rsp = self._run_request(self.urls.login, login_data, self.headers.extra)
        rsp_data = json.loads(rsp.content.decode("UTF-8"))
        if not rsp_data.get("token") or rsp_data.get("errors"):
            raise Exception("Authentication failure")
        self.headers.extra.update(
            {"X-swyConsumerDirectoryPro": self.r_s.cookies.get_dict().get("swyConsumerDirectoryPro")})
        self.headers.extra.update(
            {"X-swyConsumerlbcookie": self.r_s.cookies.get_dict().get("swyConsumerlbcookie")})

    def _get_store_id(self):
        log.info("Determining Safeway store ID")
        rsp = self._run_request(self.urls.store, data=None, headers=self.headers.extra)
        r_dict = json.loads(rsp.text)
        if str(r_dict.get("code")).lower() != "success":
            log.warn("Unable to retrieve store ID, might not be able to pull all coupons back")
        else:
            definitions = r_dict.get("definitions")[0]
            self.store_id = definitions.get("values")[0]
        log.info("Determined store ID [{0}] as the preferred store".format(self.store_id))

    def _get_unclipped_coupon_list(self):
        rsp = self._run_request(self.urls.listing.format(self.store_id), headers=nt_merge(self.headers))
        coupons = json.loads(rsp.text)
        self.raw_unclipped_list = [offer for offer in coupons["offers"] if
                                   offer["clipStatus"] != "C" and offer["listStatus"] != "S"]
        log.info("Retrieved [{0}] unclipped coupons".format(len(self.raw_unclipped_list)))

    def _get_coupon_details(self):
        for coupon in self.raw_unclipped_list:
            detailed_coupon = dict(oid=coupon["offerId"],
                                   ots=coupon["offerTs"],
                                   opgm=coupon["offerPgm"])
            url = self.urls.details.format(detailed_coupon.get("oid"), detailed_coupon.get("ots"))
            rsp = self._run_request(url, headers=nt_merge(self.headers))
            coupon_data = json.loads(rsp.content.decode("UTF-8"))
            detailed_coupon.update(dict(vbc=coupon_data.get("vndrBannerCd", "")))
            extra_cpn_detail = self._extract_coupon_detail(coupon_data)
            detailed_coupon.update(extra_cpn_detail)
            self.prepared_coupon_list.append(detailed_coupon)
        log.info("Retrieved details for {} coupons".format(len(self.prepared_coupon_list)))

    def _post_coupon(self):
        self.headers.extra.update({"Referer": self.urls.referer})
        for index, coupon in enumerate(self.prepared_coupon_list):
            items = json.dumps({
                "items": [{
                    "clipType": "C",
                    "itemId": coupon["oid"],
                    "itemType": coupon["opgm"],
                    "vndrBannerCd": coupon["vbc"]
                }, {
                    "clipType": "L",
                    "itemId": coupon["oid"],
                    "itemType": coupon["opgm"]
                }]})
            rsp = self._run_request(self.urls.post, data=items, headers=nt_merge(self.headers))
            rsp.stream = False
            if rsp.status_code == (200 or 204):
                rsp_dict = json.loads(rsp.text)

                for response in rsp_dict.get("items"):
                    if "errorCd" not in response:
                        coupon["added"] = True
                        log.info("Successfully added coupon [{}] [{}]".format(coupon["title"], coupon["value"]).encode('utf-8').strip())
                        break
                    else:
                        log.error("Unable to add Coupon ID [{0}] || Error Code [{1}] || Error Message [{2}]".format(
                            coupon["oid"], response.get("errorCd"), response.get("errorMsg")).encode("utf-8").strip())
                        break
            self._mock_delayer(index + 1)

    def _extract_coupon_detail(self, coupon_data):
        coupon_detail = coupon_data.get("offerDetail", {})
        title = "{} {} {}".format(coupon_detail.get("titleDsc1", "").strip(),
                                  coupon_detail.get("titleDsc2", "").strip(),
                                  coupon_detail.get("prodDsc1", "")).strip()
        savings_value = "{}".format(coupon_detail.get("savingsValue", "")).strip()
        price_value = "{}".format(coupon_detail.get("priceValue1", "")).strip()
        true_value = savings_value if savings_value else price_value
        expiration = self._extract_coupon_expiration(coupon_data)
        full_coupon = {"title": title, "value": true_value, "added": False, "expiration": expiration}
        log.debug("The following coupon was processed successfully {}".format(json.dumps(full_coupon, indent=4)))
        return full_coupon

    def _extract_coupon_expiration(self, coupon_data):
        try:
            end_date_regex = "\\\/Date\(([0-9]{10})[0-9]{3}\)\\\/"
            expire_ts_unix = int(re.match(end_date_regex, coupon_data["offerEndDt"]).group(1))
            expires = (datetime.datetime.fromtimestamp(expire_ts_unix)
                       .strftime("%-m/%-d/%Y"))
        except Exception as e:
            log.error(e, "Exception getting coupon details")
            expires = "Unknown"
        return expires
