import argparse
import http.cookiejar as cj
import re
import time


# Added in 3.9
class BooleanOptionalAction(argparse.Action):
    def __init__(
        self,
        option_strings,
        dest,
        default=None,
        type=None,
        choices=None,
        required=False,
        help=None,
        metavar=None,
    ):
        _option_strings = []
        for option_string in option_strings:
            _option_strings.append(option_string)

            if option_string.startswith("--"):
                option_string = "--no-" + option_string[2:]
                _option_strings.append(option_string)

        if (
            help is not None
            and default is not None
            and default is not argparse.SUPPRESS
        ):
            help += " (default: %(default)s)"

        super().__init__(
            option_strings=_option_strings,
            dest=dest,
            nargs=0,
            default=default,
            type=type,
            choices=choices,
            required=required,
            help=help,
            metavar=metavar,
        )

    def __call__(self, parser, namespace, values, option_string=None):
        if option_string in self.option_strings:
            setattr(namespace, self.dest, not option_string.startswith("--no-"))

    def format_usage(self):
        return " | ".join(self.option_strings)


# In python < 3.10, MozillaCookieJar ignores
# cookie lines starting with #HttpOnly_
# https://github.com/python/cpython/issues/46443

HTTPONLY_ATTR = "HTTPOnly"
HTTPONLY_PREFIX = "#HttpOnly_"
NETSCAPE_MAGIC_RGX = re.compile("#( Netscape)? HTTP Cookie File")
MISSING_FILENAME_TEXT = (
    "a filename was not supplied (nor was the CookieJar "
    "instance initialised with one)"
)
NETSCAPE_HEADER_TEXT = """\
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file!  Do not edit.

"""


class MozillaCookieJar(cj.FileCookieJar):
    def _really_load(self, f, filename, ignore_discard, ignore_expires):
        now = time.time()

        if not NETSCAPE_MAGIC_RGX.match(f.readline()):
            raise cj.LoadError(
                "%r does not look like a Netscape format cookies file" % filename
            )

        try:
            while 1:
                line = f.readline()
                rest = {}

                if line == "":
                    break

                # httponly is a cookie flag as defined in rfc6265
                # when encoded in a netscape cookie file,
                # the line is prepended with "#HttpOnly_"
                if line.startswith(HTTPONLY_PREFIX):
                    rest[HTTPONLY_ATTR] = ""
                    line = line[len(HTTPONLY_PREFIX) :]

                if line.endswith("\n"):
                    line = line[:-1]

                if line.strip().startswith(("#", "$")) or line.strip() == "":
                    continue

                domain, domain_specified, path, secure, expires, name, value = (
                    line.split("\t")
                )
                secure = secure == "TRUE"
                domain_specified = domain_specified == "TRUE"
                if name == "":
                    name = value
                    value = None

                initial_dot = domain.startswith(".")
                assert domain_specified == initial_dot

                discard = False
                if expires == "":
                    expires = None
                    discard = True

                c = cj.Cookie(
                    0,
                    name,
                    value,
                    None,
                    False,
                    domain,
                    domain_specified,
                    initial_dot,
                    path,
                    False,
                    secure,
                    expires,
                    discard,
                    None,
                    None,
                    rest,
                )
                if not ignore_discard and c.discard:
                    continue
                if not ignore_expires and c.is_expired(now):
                    continue
                self.set_cookie(c)

        except OSError:
            raise
        except Exception:
            cj._warn_unhandled_exception()
            raise cj.LoadError(
                "invalid Netscape format cookies file %r: %r" % (filename, line)
            )

    def save(self, filename=None, ignore_discard=False, ignore_expires=False):
        if filename is None:
            if self.filename is not None:
                filename = self.filename
            else:
                raise ValueError(cj.MISSING_FILENAME_TEXT)

        with open(filename, "w") as f:
            f.write(NETSCAPE_HEADER_TEXT)
            now = time.time()
            for cookie in self:
                domain = cookie.domain
                if not ignore_discard and cookie.discard:
                    continue
                if not ignore_expires and cookie.is_expired(now):
                    continue
                if cookie.secure:
                    secure = "TRUE"
                else:
                    secure = "FALSE"
                if domain.startswith("."):
                    initial_dot = "TRUE"
                else:
                    initial_dot = "FALSE"
                if cookie.expires is not None:
                    expires = str(cookie.expires)
                else:
                    expires = ""
                if cookie.value is None:
                    name = ""
                    value = cookie.name
                else:
                    name = cookie.name
                    value = cookie.value
                if cookie.has_nonstandard_attr(HTTPONLY_ATTR):
                    domain = HTTPONLY_PREFIX + domain
                f.write(
                    "\t".join(
                        [domain, initial_dot, cookie.path, secure, expires, name, value]
                    )
                    + "\n"
                )
