# Copyright 2023 Canonical Ltd.
# Licensed under the Apache V2, see LICENCE file for details.

import datetime
import http.cookiejar as cookiejar
import json
import time

from backports.datetime_fromisoformat import datetime_fromisoformat


class GoCookieJar(cookiejar.FileCookieJar):
    """A CookieJar implementation that reads and writes cookies
    to the cookiejar format as understood by the Go package
    github.com/juju/persistent-cookiejar.
    """

    def _really_load(self, f, filename, ignore_discard, ignore_expires):
        """Implement the _really_load method called by FileCookieJar
        to implement the actual cookie loading
        """
        data = json.load(f) or []
        now = time.time()
        for cookie in map(go_to_py_cookie, data):
            if not ignore_expires and cookie.is_expired(now):
                continue
            self.set_cookie(cookie)

    def save(self, filename=None, ignore_discard=False, ignore_expires=False):
        """Implement the FileCookieJar abstract method."""
        if filename is None:
            if self.filename is not None:
                filename = self.filename
            else:
                raise ValueError(cookiejar.MISSING_FILENAME_TEXT)

        # TODO: obtain file lock, read contents of file, and merge with
        # current content.
        go_cookies = []
        now = time.time()
        for cookie in self:
            if not ignore_discard and cookie.discard:
                continue
            if not ignore_expires and cookie.is_expired(now):
                continue
            go_cookies.append(py_to_go_cookie(cookie))
        with open(filename, "w") as f:
            f.write(json.dumps(go_cookies))


def go_to_py_cookie(go_cookie):
    """Convert a Go-style JSON-unmarshaled cookie into a Python cookie"""
    expires = None
    if go_cookie.get("Expires") is not None:
        t = datetime_fromisoformat(go_cookie["Expires"])
        expires = t.timestamp()
    return cookiejar.Cookie(
        version=0,
        name=go_cookie["Name"],
        value=go_cookie["Value"],
        port=None,
        port_specified=False,
        # Unfortunately Python cookies don't record the original
        # host that the cookie came from, so we'll just use Domain
        # for that purpose, and record that the domain was specified,
        # even though it probably was not. This means that
        # we won't correctly record the CanonicalHost entry
        # when writing the cookie file after reading it.
        domain=go_cookie["Domain"],
        domain_specified=not go_cookie["HostOnly"],
        domain_initial_dot=False,
        path=go_cookie["Path"],
        path_specified=True,
        secure=go_cookie["Secure"],
        expires=expires,
        discard=False,
        comment=None,
        comment_url=None,
        rest=None,
        rfc2109=False,
    )


def py_to_go_cookie(py_cookie):
    """Convert a python cookie to the JSON-marshalable Go-style cookie form."""
    # TODO (perhaps):
    #   HttpOnly
    #   Creation
    #   LastAccess
    #   Updated
    # not done properly: CanonicalHost.
    go_cookie = {
        "Name": py_cookie.name,
        "Value": py_cookie.value,
        "Domain": py_cookie.domain,
        "HostOnly": not py_cookie.domain_specified,
        "Persistent": not py_cookie.discard,
        "Secure": py_cookie.secure,
        "CanonicalHost": py_cookie.domain,
    }
    if py_cookie.path_specified:
        go_cookie["Path"] = py_cookie.path
    if py_cookie.expires is not None:
        # Note: fromtimestamp bizarrely produces a time without
        # a time zone, so we need to use accept_naive.
        go_cookie["Expires"] = datetime.datetime.fromtimestamp(
            py_cookie.expires
        ).isoformat()
    return go_cookie
