# coding: utf-8
import re
import urllib.parse

from django.conf import settings
from django.core.cache import caches
from io import StringIO
from requests.exceptions import HTTPError

from events.surveyme_integration.exceptions import (
    WikiPermissionError,
    WikiSupertagError,
    WikiContentTypeError,
    WIKI_NOT_VALID_SUPERTAG_MESSAGE,
)
from events.common_app.utils import requests_session
from events.yauth_contrib.auth import TvmAuth


class WikiClient:
    def __init__(self, user_uid, dir_id=None):
        self.user_uid = user_uid
        self.dir_id = dir_id

    def _get_headers(self):
        headers = {
            'X-UID': str(self.user_uid),
        }
        if self.dir_id:
            headers['X-Org-Id'] = self.dir_id
        return headers

    def _make_request(self, url, method, data=None):
        params = {
            'uid': str(self.user_uid),
        }
        response = requests_session.request(
            method.lower(),
            url,
            timeout=settings.DEFAULT_TIMEOUT,
            verify=settings.YANDEX_ROOT_CERTIFICATE,
            headers=self._get_headers(),
            json=data,
            params=params,
            auth=TvmAuth(settings.WIKI_TVM2_CLIENT),
        )
        response.raise_for_status()
        return response

    def _path_join(self, *parts):
        p = urllib.parse.urlparse('/'.join(parts))
        return p._replace(path=re.sub(r'//+', '/', p.path)).geturl()

    def _get_page_head(self, supertag):
        url = self._path_join(settings.WIKI_BASE_URL, supertag)
        return self._make_request(url, method='head')

    def get_supertag(self, url):
        p = urllib.parse.urlparse(url)
        return p.path.strip('/')

    def is_valid_supertag(self, supertag):
        if not supertag:
            return False

        pattern = (
            r'^(?P<tag>(/?[\w\-][\w\-\.:\+]*/)*[\w\-][\w\-\.:\+]*/?)'
            r'(#[\w\-][\w\-\.:\+]*)?$'
        )
        matched = re.match(pattern, supertag)
        return matched is not None

    def is_page_exists(self, supertag):
        try:
            self._get_page_head(supertag)
        except HTTPError:
            return False
        else:
            return True

    def _find_anchor_position(self, content, anchor_name):
        pattern = r'{{\s*a(nchor)?\s+name\s*=\s*["\']?([\w\-][\w\-\.:\+]*)["\']?\s*}}[^\n]*\n'
        for it in re.finditer(pattern, content):
            if it.group(2) == anchor_name:
                return it.end()

    def _append_page_content(self, content, text):
        st = StringIO()
        st.write(content)
        st.write('\n')
        st.write(text)
        return st.getvalue()

    def _insert_page_content(self, content, text, pos):
        st = StringIO()
        st.write(content[:pos])
        st.write(text)
        st.write('\n')
        st.write(content[pos:])
        return st.getvalue()

    def _update_page_content(self, content, text, anchor_name):
        pos = None
        if anchor_name:
            pos = self._find_anchor_position(content, anchor_name)
        if pos is not None:
            content = self._insert_page_content(content, text, pos)
        else:
            content = self._append_page_content(content, text)
        return content

    def append_to_wiki_page(self, supertag, text):
        if not self.is_valid_supertag(supertag):
            raise ValueError(WIKI_NOT_VALID_SUPERTAG_MESSAGE % supertag)

        fragment = None
        if '#' in supertag:
            supertag, fragment = supertag.split('#', 1)

        page = self._get_page_content(supertag)
        old_content = page.get('body', '')
        new_content = self._update_page_content(old_content, text, fragment)
        self._set_page_content(supertag, new_content)

    def _get_page_content(self, supertag, postfix='.raw'):
        if not self.is_valid_supertag(supertag):
            raise ValueError(WIKI_NOT_VALID_SUPERTAG_MESSAGE % supertag)

        url = self._path_join(settings.WIKI_BASE_URL, supertag, postfix)
        try:
            response = self._make_request(url, method='get')
        except HTTPError as e:
            self.handle_error(e, supertag=supertag)
        else:
            return response.json()['data']

    def _set_page_content(self, supertag, text):
        if not self.is_valid_supertag(supertag):
            raise ValueError(WIKI_NOT_VALID_SUPERTAG_MESSAGE % supertag)

        data = {'body': text}
        url = self._path_join(settings.WIKI_BASE_URL, supertag)
        try:
            self._make_request(url, method='post', data=data)
        except HTTPError as e:
            self.handle_error(e, supertag=supertag)

    def get_table_data(self, supertag):
        cache = caches['default']
        cache_key = '%s:%s' % (self.dir_id, supertag)
        table_data = cache.get(cache_key)

        if not table_data:
            table_data = self._get_page_content(supertag, postfix='.grid')
            cache.set(cache_key, table_data)
        return table_data

    def handle_error(self, e, supertag=''):
        if isinstance(e, HTTPError):
            if e.response.status_code == 404:
                raise WikiSupertagError(supertag, e)
            elif e.response.status_code == 403:
                raise WikiPermissionError(self.user_uid, e)
            elif e.response.status_code == 400:
                raise WikiContentTypeError(supertag, e)
        raise


def get_robot_wiki(dir_id):
    from events.common_app.directory import CachedDirectoryClient
    client = CachedDirectoryClient()
    return client.get_service_user(dir_id, 'wiki')


def get_wiki_client(dir_id=None):
    if settings.IS_BUSINESS_SITE:
        user_uid = get_robot_wiki(dir_id)
    else:
        user_uid = settings.FORMS_ROBOT_UID
    return WikiClient(user_uid, dir_id)


def check_if_supertag_exist(supertag, dir_id=None):
    client = get_wiki_client(dir_id)
    return client.is_page_exists(supertag)
