# 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/>.

import json
from io import StringIO
from unittest import mock

import pytest
from django.core.files import File

import tests.utils
from passerelle.apps.base_adresse.models import BaseAdresse
from passerelle.apps.sector.models import SectorResource
from tests.test_manager import login

BAN = {
    "attribution": "BAN",
    "features": [
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "housenumber": "169",
                "id": "75114_1912_00169",
                "importance": 0.77751,
                "label": "169 Rue du Château 75014 Paris",
                "name": "169 Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "housenumber": "167",
                "id": "75114_1912_00167",
                "importance": 0.77751,
                "label": "167 Rue du Château 75014 Paris",
                "name": "167 Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "housenumber": "170",
                "id": "75114_1912_00169",
                "importance": 0.77751,
                "label": "170 Rue du Château 75014 Paris",
                "name": "170 Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        # no house number
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "id": "75114_1912_00169",
                "importance": 0.77751,
                "label": "XX Rue du Château 75014 Paris",
                "name": "Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "id": "75114_1913_00000",
                "importance": 0.77751,
                "label": "YY Rue du Château 75014 Paris",
                "name": "Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        # empty id (no street_id)
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "housenumber": "169",
                "id": "",  # empty id => no street_id
                "importance": 0.77751,
                "label": "169 Rue du Château 75014 Paris",
                "name": "169 Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        # id is not a string => no street_id
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "housenumber": "169",
                "id": 42,  # integer id => no street_id
                "importance": 0.77751,
                "label": "169 Rue du Château 75014 Paris",
                "name": "169 Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
        # id without "_" => no street_id
        {
            "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"},
            "properties": {
                "city": "Paris",
                "citycode": "75114",
                "context": "75, Paris, Île-de-France",
                "district": "Paris 14e Arrondissement",
                "housenumber": "169",
                "id": "123",  # no "_" => no street_id
                "importance": 0.77751,
                "label": "169 Rue du Château 75014 Paris",
                "name": "169 Rue du Château",
                "postcode": "75014",
                "score": 0.9797736363636363,
                "street": "Rue du Château",
                "type": "housenumber",
                "x": 650331.55,
                "y": 6859506.98,
            },
            "type": "Feature",
        },
    ],
    "licence": "ETALAB-2.0",
    "limit": 5,
    "query": "169 rue du chateau, 75014 Paris",
    "type": "FeatureCollection",
    "version": "draft",
}

CSV = """street_id,street_name,parity,min_housenumber,max_housenumber,sector_id,sector_name
75114_1912,rue du Château,P,,,gs-moulin,Groupe Scolaire Moulin
75114_1912,rue du Château,I,0,167,gs-zola,Groupe Scolaire Zola
75114_1912,rue du Château,I,168,999999,ecole-hugo,École Hugo
75114_1913,rue 1913,,,,ecole-pascal,École Pascal
"""


@pytest.fixture
def sector(db):
    return tests.utils.setup_access_rights(
        SectorResource.objects.create(
            slug='ecole',
            title='Secteur scolaire',
            description='desc',
            csv_file=File(StringIO(CSV), 'sectorization.csv'),
        )
    )


@pytest.fixture
def base_adresse(db):
    return tests.utils.setup_access_rights(BaseAdresse.objects.create(slug='base-adresse', zipcode='75114'))


@mock.patch('passerelle.utils.Request.get')
def test_sectorization(mocked_get, app, base_adresse, sector):
    assert base_adresse.sectors.count() == 0
    mocked_get.return_value = tests.utils.FakedResponse(content=json.dumps(BAN), status_code=200)
    url = tests.utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug)
    addresses = app.get(url, params={'q': 'foo'}, status=200).json
    assert len(addresses) == 8
    for address in addresses:
        assert 'sectors' not in address
        assert 'sectorization_error' not in address

    base_adresse.sectors.add(sector)

    # empty sector
    sector.sector_set.all().delete()
    addresses = app.get(url, params={'q': 'foo'}, status=200).json
    assert len(addresses) == 8
    for address in addresses:
        assert address['sectors'] == {}  # sector is empty

    sector.save()  # reload CSV data in sector
    addresses = app.get(url, params={'q': 'foo'}, status=200).json
    assert addresses[0]['sectors'] == {'ecole': {'id': 'ecole-hugo', 'text': 'École Hugo'}}
    assert addresses[1]['sectors'] == {'ecole': {'id': 'gs-zola', 'text': 'Groupe Scolaire Zola'}}
    assert addresses[2]['sectors'] == {'ecole': {'id': 'gs-moulin', 'text': 'Groupe Scolaire Moulin'}}
    # addresses without housenumber
    assert addresses[3]['sectors'] == {}
    assert addresses[4]['sectors'] == {'ecole': {'id': 'ecole-pascal', 'text': 'École Pascal'}}
    # bad address format, no street_id
    assert addresses[5]['sectors'] == {}
    assert addresses[5]['sectorization_error'] == 'missing street_id in address'
    assert addresses[6]['sectors'] == {}
    assert addresses[6]['sectorization_error'] == 'missing street_id in address'
    assert addresses[7]['sectors'] == {}
    assert addresses[7]['sectorization_error'] == 'missing street_id in address'

    url = tests.utils.generic_endpoint_url('base-adresse', 'sectors', slug=base_adresse.slug)
    for params in (None, {'id': 'ecole'}, {'q': 'scolaire'}):
        sectors = app.get(url, params=params, status=200).json
        assert sectors['err'] == 0
        assert sectors['data'] == [
            {
                'id': 'ecole',
                'text': 'Secteur scolaire',
                'sectors_url': 'http://localhost/sector/ecole/sectors/',
                'description': 'desc',
            }
        ]
    for params in ({'id': 'foo'}, {'q': 'foo'}):
        sectors = app.get(url, params=params, status=200).json
        assert sectors['err'] == 0
        assert sectors['data'] == []


def test_manager_with_or_without_sectors(db, app, admin_user, base_adresse, sector):
    app = login(app)
    path = '/manage/%s/add' % base_adresse.get_connector_slug()
    resp = app.get(path)
    assert 'Sectorizations' in str(resp.form.html)
    assert 'Secteur scolaire [ecole]' in str(resp.form.html)

    sector.delete()
    resp = app.get(path)
    assert 'Sectorizations' not in str(resp.form.html)
    assert 'Secteur scolaire [ecole]' not in str(resp.form.html)
