# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2021  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 urljoin

from django.conf import settings
from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _

from passerelle.apps.sector.models import (
    MAX_HOUSENUMBER,
    PARITY_ALL,
    PARITY_EVEN,
    PARITY_ODD,
    Sectorization,
    SectorResource,
)
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint


class AddressResource(BaseResource):
    sectors = models.ManyToManyField(
        SectorResource, blank=True, related_name='resources', verbose_name=_('Sectorizations')
    )

    category = _('Geographic information system')

    class Meta:
        abstract = True

    @classmethod
    def get_manager_form_class(cls, **kwargs):
        if not SectorResource.objects.exists():
            kwargs['exclude'] = tuple(kwargs.get('exclude') or ()) + ('sectors',)
        return super().get_manager_form_class(**kwargs)

    def export_json(self):
        d = super().export_json()
        d['sectors'] = [sector.slug for sector in self.sectors.all()]
        return d

    @classmethod
    def import_json_real(cls, overwrite, instance, d, **kwargs):
        sectors = d.pop('sectors', [])
        instance = super().import_json_real(overwrite, instance, d, **kwargs)
        if instance and overwrite:
            instance.sectors.clear()
        for slug in sectors:
            sector = SectorResource.objects.filter(slug=slug).first()
            if sector:
                instance.sectors.add(sector)
        return instance

    def sectorize(self, address):
        """
        add 'sectors' entry in address.
        address is a Nominatim formatted dict and should contain 'street_id' and
        'house_number' in its 'address' dict.
        """
        if not self.sectors.exists():
            return
        address['sectors'] = {}

        street_id = address['address'].get('street_id')
        if not street_id:
            address['sectorization_error'] = 'missing street_id in address'
            return

        try:
            house_number = int(address['address'].get('house_number'))
        except (TypeError, ValueError):
            house_number = None

        query = Sectorization.objects.filter(sector__resource__in=self.sectors.all(), street_id=street_id)
        if house_number is not None:
            query = query.filter(min_housenumber__lte=house_number, max_housenumber__gte=house_number)
            parity = PARITY_ODD if house_number % 2 else PARITY_EVEN
            query = query.filter(Q(parity=PARITY_ALL) | Q(parity=parity))
        else:
            query = query.filter(parity=PARITY_ALL, min_housenumber=0, max_housenumber=MAX_HOUSENUMBER)
        for sectorization in query.reverse():
            address['sectors'][sectorization.sector.resource.slug] = {
                'id': sectorization.sector.slug,
                'text': sectorization.sector.title,
            }

    @endpoint(
        name='sectors',
        description=_('List related Sectorizations'),
        parameters={
            'id': {'description': _('Sector Identifier (slug)')},
            'q': {'description': _('Filter by Sector Title or Identifier')},
        },
    )
    def sectors_(self, request, q=None, id=None):
        query = self.sectors.all()
        if id is not None:
            query = query.filter(slug=id)
        elif q is not None:
            query = query.filter(Q(slug__icontains=q) | Q(title__icontains=q))
        return {
            'data': [
                {
                    'id': resource.slug,
                    'text': resource.title,
                    'description': resource.description,
                    'sectors_url': urljoin(
                        settings.SITE_BASE_URL, '%ssectors/' % resource.get_absolute_url()
                    ),
                }
                for resource in query
            ]
        }
