# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2019  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 django.contrib.postgres.fields import JSONField
from django.db import models
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.http import urlencode
from django.utils.timezone import make_naive, now, timedelta
from django.utils.translation import gettext_lazy as _

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


class PhoneCalls(BaseResource):
    category = _('Telephony')

    max_call_duration = models.PositiveIntegerField(
        _('Maximum duration of a call, in minutes.'),
        help_text=_('Each hour, too long calls are closed.'),
        default=120,
    )
    data_retention_period = models.PositiveIntegerField(
        _('Data retention period, in days.'), help_text=_('Each day, old calls are removed.'), default=60
    )
    redirect_url = models.URLField(
        verbose_name=_('URL to redirect to or open when using redirect or newtab parameters'),
        null=True,
        blank=True,
    )

    class Meta:
        verbose_name = _('Phone Calls')

    @endpoint(
        name='call-start',
        description=_('Notify a call start'),
        perm='can_access',
        parameters={
            'callee': {'description': _('Callee number'), 'example_value': '142'},
            'caller': {'description': _('Caller number'), 'example_value': '0143350135'},
            'redirect': {'description': _('Redirect browser to configured URL')},
            'newtab': {'description': _('Open configured URL in a new tab')},
        },
    )
    def call_start(self, request, callee, caller, redirect=None, newtab=None, **kwargs):
        new_call = Call(resource=self, callee=callee, caller=caller, details=kwargs)
        new_call.save()
        response = {'data': new_call.json()}

        redirect_url = self.redirect_url
        if redirect_url:
            redirect_url += ('&' if '?' in redirect_url else '?') + urlencode({'callee': callee})

        # redirect to agent's portal
        if redirect and redirect_url:
            return HttpResponseRedirect(redirect_url)

        # open agent's portal in a new tab
        if newtab and redirect_url:
            return render(request, 'phonecalls/start_call_newtab.html', {'redirect_url': redirect_url})
        return response

    @endpoint(
        name='call-stop',
        description=_('Notify a call end'),
        perm='can_access',
        parameters={
            'callee': {'description': _('Callee number'), 'example_value': '142'},
            'caller': {'description': _('Caller number'), 'example_value': '0143350135'},
        },
    )
    def call_stop(self, request, callee, caller, **kwargs):
        # close all current callee/caller calls
        data = []
        for current_call in Call.objects.filter(
            resource=self, callee=callee, caller=caller, end_timestamp=None
        ):
            current_call.end_timestamp = now()
            current_call.save()
            data.append(current_call.json())
        return {'data': data}

    @endpoint(
        name='calls',
        description=_('Get list of calls to a line'),
        perm='can_access',
        parameters={
            'callee': {'description': _('Callee number'), 'example_value': '142'},
            'limit': {'description': _('Maximal number of results')},
        },
    )
    def calls(self, request, callee=None, caller=None, limit=30):
        calls = Call.objects.filter(resource=self)
        if callee:
            calls = calls.filter(callee=callee)
        if caller:
            calls = calls.filter(caller=caller)

        def json_list(calls):
            return [call.json() for call in calls[:limit]]

        return {
            'data': {
                'current': json_list(calls.filter(end_timestamp__isnull=True)),
                'past': json_list(calls.filter(end_timestamp__isnull=False)),
            }
        }

    def hourly(self):
        super().hourly()
        # close unfinished long calls
        maximal_time = now() - timedelta(minutes=self.max_call_duration)
        Call.objects.filter(resource=self, end_timestamp=None, start_timestamp__lt=maximal_time).update(
            end_timestamp=now()
        )

    def daily(self):
        super().daily()
        # remove finished old calls
        maximal_time = now() - timedelta(days=self.data_retention_period)
        Call.objects.filter(
            resource=self, end_timestamp__isnull=False, end_timestamp__lt=maximal_time
        ).delete()


class Call(models.Model):
    resource = models.ForeignKey(PhoneCalls, on_delete=models.CASCADE)
    callee = models.CharField(blank=False, max_length=64)
    caller = models.CharField(blank=False, max_length=64)
    start_timestamp = models.DateTimeField(auto_now_add=True)
    end_timestamp = models.DateTimeField(null=True, default=None)
    details = JSONField(default=dict)

    class Meta:
        verbose_name = _('Phone Call')
        ordering = ['-start_timestamp']

    def json(self):
        # We use make_naive to send localtime, because this API will be used
        # by javascript, which will not be comfortable with UTC datetimes
        if self.end_timestamp:
            is_current = False
            end_timestamp = make_naive(self.end_timestamp)
        else:
            is_current = True
            end_timestamp = None
        return {
            'caller': self.caller,
            'callee': self.callee,
            'start': make_naive(self.start_timestamp),
            'end': end_timestamp,
            'is_current': is_current,
            'details': self.details,
        }
