"""Shared job location form fields and validation."""
from django import forms

from .service import (
    DEFAULT_COUNTRY_CODE,
    city_choices,
    compose_job_location,
    compose_location,
    country_choices,
    get_cities,
    get_regions,
    parse_location,
    region_valid,
)
from .zambia_geo import normalize_zambia_region

LOCATION_SCOPE_SINGLE = 'single'
LOCATION_SCOPE_MULTI = 'multi'


def normalize_location_post_data(data):
    """Map legacy Zambia province names in POST data to the current labels."""
    if not data:
        return data
    mutable = data.copy()
    country = (mutable.get('location_country') or DEFAULT_COUNTRY_CODE).upper()
    if country == 'ZM':
        if mutable.get('location_region'):
            mutable['location_region'] = normalize_zambia_region(mutable['location_region'])
        if hasattr(mutable, 'setlist'):
            multi = mutable.getlist('location_regions_multi')
            if multi:
                mutable.setlist(
                    'location_regions_multi',
                    [normalize_zambia_region(region) for region in multi],
                )
    return mutable


def _normalize_region_list(country, regions):
    country = (country or DEFAULT_COUNTRY_CODE).upper()
    cleaned = [region.strip() for region in regions if region and region.strip()]
    if country == 'ZM':
        return [normalize_zambia_region(region) for region in cleaned]
    return cleaned


def validate_single_job_location(country, region, city):
    """Return composed location string and a dict of field errors."""
    country = (country or DEFAULT_COUNTRY_CODE).upper()
    region = (region or '').strip()
    if country == 'ZM':
        region = normalize_zambia_region(region)
    city = (city or '').strip()
    errors = {}
    if not region:
        errors['location_region'] = 'Select a province or region.'
    elif not region_valid(country, region):
        errors['location_region'] = 'Select a valid province or region for this country.'
    if not city:
        errors['location_city'] = 'Select a city or town.'
    elif region_valid(country, region):
        cities = get_cities(country, region)
        if cities and city not in cities:
            errors['location_city'] = 'Select a valid city or town for this region.'
    if errors:
        return '', {}, []
    return compose_location(city, region, country), {}, [region]


def validate_multi_job_location(country, regions, city_text):
    """Return composed location string and field errors for multi-region jobs."""
    country = (country or DEFAULT_COUNTRY_CODE).upper()
    city = (city_text or '').strip()
    regions = _normalize_region_list(country, regions)
    errors = {}
    if not regions:
        errors['location_regions_multi'] = 'Select at least one province or region.'
    else:
        invalid = [region for region in regions if not region_valid(country, region)]
        if invalid:
            errors['location_regions_multi'] = 'Select valid provinces or regions for this country.'
    if errors:
        return '', {}, []
    display = compose_job_location(city, regions, country)
    return display, {}, regions


class JobLocationFormMixin(forms.Form):
    location_scope = forms.ChoiceField(
        label='Location scope',
        choices=[
            (LOCATION_SCOPE_SINGLE, 'Single location'),
            (LOCATION_SCOPE_MULTI, 'Multiple regions'),
        ],
        initial=LOCATION_SCOPE_SINGLE,
        widget=forms.RadioSelect(attrs={'class': 'location-scope-radio'}),
    )
    location_country = forms.ChoiceField(
        label='Country',
        choices=country_choices(),
        widget=forms.Select(attrs={'class': 'form-control', 'id': 'id_location_country'}),
    )
    location_region = forms.CharField(
        label='Province / Region',
        required=False,
        widget=forms.Select(
            choices=[('', 'Select province / region')],
            attrs={'class': 'form-control', 'id': 'id_location_region'},
        ),
    )
    location_city = forms.CharField(
        label='City / Town',
        required=False,
        widget=forms.Select(
            choices=[('', 'Select city / town')],
            attrs={'class': 'form-control', 'id': 'id_location_city'},
        ),
    )
    location_regions_multi = forms.MultipleChoiceField(
        label='Provinces / Regions',
        choices=[],
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={'class': 'location-regions-multi'}),
    )
    location_city_text = forms.CharField(
        label='City / Town (optional)',
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'id': 'id_location_city_text',
            'placeholder': 'e.g. head office, various, or leave blank',
            'autocomplete': 'off',
        }),
    )

    def _region_choices(self, country_code):
        return [('', 'Select province / region')] + [
            (region['name'], region['name']) for region in get_regions(country_code)
        ]

    def _multi_region_choices(self, country_code):
        return [(region['name'], region['name']) for region in get_regions(country_code)]

    def _city_choices(self, country_code, region_name):
        choices = city_choices(country_code, region_name)
        return choices or [('', 'Select city / town')]

    def _instance_location_state(self, company=None):
        instance = getattr(self, 'instance', None)
        country = DEFAULT_COUNTRY_CODE
        scope = LOCATION_SCOPE_SINGLE
        region = ''
        city = ''
        multi_regions = []
        city_text = ''

        if instance and getattr(instance, 'pk', None):
            stored_regions = list(getattr(instance, 'location_regions', None) or [])
            country = (getattr(instance, 'location_country', None) or DEFAULT_COUNTRY_CODE).upper()
            city_text = (getattr(instance, 'location_city', None) or '').strip()
            if len(stored_regions) > 1:
                scope = LOCATION_SCOPE_MULTI
                multi_regions = stored_regions
            elif len(stored_regions) == 1:
                region = stored_regions[0]
                city = city_text
            elif instance.location:
                parsed = parse_location(instance.location)
                country = parsed['country'] or country
                region = parsed['region']
                city = parsed['city'] or city_text
        elif company and getattr(company, 'location', None):
            parsed = parse_location(company.location)
            country = parsed['country'] or country
            region = parsed['region']
            city = parsed['city']

        return {
            'country': country,
            'scope': scope,
            'region': region,
            'city': city,
            'multi_regions': multi_regions,
            'city_text': city_text if scope == LOCATION_SCOPE_MULTI else '',
        }

    def init_job_location_fields(self, company=None):
        state = self._instance_location_state(company=company)

        if self.data:
            country = (self.data.get('location_country') or DEFAULT_COUNTRY_CODE).upper()
            scope = self.data.get('location_scope') or LOCATION_SCOPE_SINGLE
            region = (self.data.get('location_region') or '').strip()
        else:
            country = state['country']
            scope = state['scope']
            region = state['region']

        self.fields['location_scope'].initial = scope
        self.fields['location_country'].initial = country
        region_choices = self._region_choices(country)
        self.fields['location_region'].widget.choices = region_choices
        self.fields['location_regions_multi'].choices = self._multi_region_choices(country)

        city_choices_list = self._city_choices(country, region)
        if not self.data and state['city']:
            city_values = {value for value, _ in city_choices_list if value}
            if state['city'] not in city_values:
                city_choices_list = city_choices_list + [(state['city'], state['city'])]
        self.fields['location_city'].widget.choices = city_choices_list

        if not self.data:
            region_initial = state['region']
            if country == 'ZM':
                region_initial = normalize_zambia_region(region_initial)
            multi_initial = state['multi_regions']
            if country == 'ZM':
                multi_initial = [normalize_zambia_region(item) for item in multi_initial]
            self.fields['location_region'].initial = region_initial
            self.fields['location_city'].initial = state['city']
            self.fields['location_regions_multi'].initial = multi_initial
            self.fields['location_city_text'].initial = state['city_text']

        for field_name in (
            'location_country',
            'location_region',
            'location_city',
            'location_regions_multi',
            'location_city_text',
        ):
            self.fields[field_name].help_text = ''

    def apply_job_location_validation(self, cleaned_data):
        job_type = (cleaned_data.get('job_type') or '').strip()
        if not job_type:
            return cleaned_data

        country = cleaned_data.get('location_country') or DEFAULT_COUNTRY_CODE
        scope = cleaned_data.get('location_scope') or LOCATION_SCOPE_SINGLE

        if scope == LOCATION_SCOPE_MULTI:
            regions = cleaned_data.get('location_regions_multi') or []
            city_text = cleaned_data.get('location_city_text') or ''
            display, errors, region_list = validate_multi_job_location(country, regions, city_text)
            for field, message in errors.items():
                self.add_error(field, message)
            if display and not errors:
                cleaned_data['location'] = display
                cleaned_data['location_country'] = country.upper()
                cleaned_data['location_regions_list'] = region_list
                cleaned_data['location_city_stored'] = city_text.strip()
            return cleaned_data

        region = cleaned_data.get('location_region') or ''
        city = cleaned_data.get('location_city') or ''
        display, errors, region_list = validate_single_job_location(country, region, city)
        for field, message in errors.items():
            self.add_error(field, message)
        if display and not errors:
            cleaned_data['location'] = display
            cleaned_data['location_country'] = country.upper()
            cleaned_data['location_regions_list'] = region_list
            cleaned_data['location_city_stored'] = city.strip()
        return cleaned_data

    def persist_structured_location(self, job):
        if not getattr(self, 'cleaned_data', None):
            return job
        job.location_country = self.cleaned_data.get('location_country') or DEFAULT_COUNTRY_CODE
        job.location_regions = self.cleaned_data.get('location_regions_list') or []
        job.location_city = self.cleaned_data.get('location_city_stored') or ''
        return job
