# -*- coding: utf-8 -*-
"""
itb_www
=======
"""

__all__ = [
    'ZOPE_DATA_TYPES',
    'change_query_string',
]

import urllib.parse
from typing import Literal, Optional

try:
    from typing import assert_never
except ImportError:
    from typing_extensions import assert_never

ZOPE_DATA_TYPES = {
    'boolean',
    'int',
    'long',
    'float',
    'string',
    'text',
    'list',
    'tuple',
    'tokens',
    'lines',
    ':ignore_empty'
}


def change_query_string(
    *,
    params: dict,
    url: Optional[str] = None,
    query_string: Optional[str] = None,
    delete_remaining: bool = False,
    returning: Literal['url', 'query_string', 'auto'] = 'auto'
) -> str:
    """Change arguments in a query string

    There are no positional arguments, because it must be clear
    if an url or a query string is provided.

    :param params: dictionary of query parameters to set
    :param url: full URL whose query string should be modified
    :param query_string: raw query string to modify (alternative to ``url``)
    :param delete_remaining: only keep arguments specified in params
    :param returning: ``'url'``, ``'query_string'`` or ``'auto'`` (inferred from input)
    :return: modified URL or query string
    """
    if returning == 'auto':
        if url:
            returning = 'url'
        elif query_string:
            returning = 'query_string'

    if url:
        url_parts = urllib.parse.urlparse(url)
        query = dict(urllib.parse.parse_qsl(url_parts.query))
    elif query_string:
        if not url and returning == 'url':
            raise ValueError('an url is needed to return an url (only query string provided)')
        query = dict(urllib.parse.parse_qsl(query_string))
    else:
        raise TypeError('change_query_string needs an "url" or "query_string" argument!')

    if delete_remaining:
        query = params
    else:
        query.update(params)

    new_query_string = urllib.parse.urlencode(query)
    if '%3A' in new_query_string:
        for typ in ZOPE_DATA_TYPES:
            new_query_string = new_query_string.replace(f'%3A{typ}', f':{typ}')

    if returning == 'query_string':
        return new_query_string

    if returning == 'url':
        new_url = url_parts._replace(query=new_query_string).geturl()
        return new_url

    assert_never(returning)  # unreachable
