# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2023 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from urllib.parse import parse_qsl

from django.db import models
from django.http.response import HttpResponse
from django.utils.translation import gettext_lazy as _

from passerelle.base.models import BaseResource, HTTPResource
from passerelle.utils.api import endpoint

PASS_HEADERS_REQUEST = (
    'accept',
    'accept-encoding',
    'accept-language',
    'cookie',
    'user-agent',
)


class Resource(BaseResource, HTTPResource):
    category = _('Misc')

    upstream_base_url = models.URLField(_('Upstream Service Base URL'))
    http_timeout = models.PositiveIntegerField(_('Timeout on upstream (in seconds)'), default=20)
    forced_headers = models.TextField(
        _('Headers'),
        blank=True,
        help_text=_('Headers to always add (one per line, format "Header-Name: value")'),
    )

    class Meta:
        verbose_name = _('Proxy')

    @endpoint(
        name='request',
        perm='can_access',
        methods=['get', 'post', 'delete', 'put', 'patch'],
        pattern=r'^(?P<path>.*)$',
        description=_('Make a request'),
        example_pattern='{path}',
        parameters={
            'path': {
                'description': _('request will be made on Upstream Service Base URL + path'),
                'example_value': 'foo/bar',
            }
        },
    )
    def request(self, request, path, *args, **kwargs):
        params = parse_qsl(request.META.get('QUERY_STRING'))
        if params and params[-1][0] == 'signature':
            # remove Publik signature parts: orig, algo, timestamp, nonce, signature
            params = [
                (k, v) for k, v in params if k not in ('orig', 'algo', 'timestamp', 'nonce', 'signature')
            ]
        headers = {k: v for k, v in request.headers.items() if v and k.lower() in PASS_HEADERS_REQUEST}
        if request.method != 'GET':
            headers['Content-Type'] = request.headers.get('content-type')
        for header in self.forced_headers.splitlines():
            header = header.strip()
            if header.startswith('#'):
                continue
            header = header.split(':', 1)
            if len(header) == 2:
                headers[header[0].strip()] = header[1].strip()
        upstream = self.requests.request(
            method=request.method,
            url=self.upstream_base_url + path,
            headers=headers,
            params=params,
            data=request.body,
            timeout=self.http_timeout,
        )
        response = HttpResponse(
            upstream.content,
            content_type=upstream.headers.get('Content-Type'),
            status=upstream.status_code,
            reason=upstream.reason,
        )
        return response
