"""Country / province / city data for job location fields."""
from __future__ import annotations

import json
from functools import lru_cache
from pathlib import Path

DATA_DIR = Path(__file__).resolve().parent / 'data'
DEFAULT_COUNTRY_CODE = 'ZM'


def _normalize_region_name(country_code: str, region_name: str) -> str:
    code = (country_code or '').upper()
    region_name = (region_name or '').strip()
    if code == 'ZM' and region_name:
        from .zambia_geo import normalize_zambia_region

        return normalize_zambia_region(region_name)
    return region_name


@lru_cache(maxsize=1)
def _countries() -> list[dict]:
    return json.loads((DATA_DIR / 'countries.json').read_text(encoding='utf-8'))


@lru_cache(maxsize=1)
def _regions_by_country() -> dict[str, list[dict]]:
    return json.loads((DATA_DIR / 'regions_by_country.json').read_text(encoding='utf-8'))


@lru_cache(maxsize=1)
def _cities_by_region() -> dict[str, list[str]]:
    path = DATA_DIR / 'cities_by_region.json'
    if not path.exists():
        return {}
    return json.loads(path.read_text(encoding='utf-8'))


def country_choices(blank_label='Select country'):
    return [('', blank_label)] + [(item['code'], item['name']) for item in _countries()]


def get_country_name(code: str) -> str:
    code = (code or '').upper()
    for item in _countries():
        if item['code'] == code:
            return item['name']
    return code


def get_regions(country_code: str) -> list[dict]:
    code = (country_code or '').upper()
    if code == 'ZM':
        from .zambia_geo import zambia_region_entries

        return list(zambia_region_entries())
    return list(_regions_by_country().get(code, []))


def region_valid(country_code: str, region_name: str) -> bool:
    region_name = _normalize_region_name(country_code, region_name)
    if not region_name:
        return False
    names = {region['name'].casefold() for region in get_regions(country_code)}
    codes = {region['code'].casefold() for region in get_regions(country_code)}
    key = region_name.casefold()
    return key in names or key in codes


def _city_lookup_keys(country_code: str, region_name: str) -> list[str]:
    code = (country_code or '').upper()
    region_name = _normalize_region_name(code, region_name)
    region = next(
        (item for item in get_regions(code) if item['name'].casefold() == region_name.casefold()),
        None,
    )
    keys = [f'{code}|{region_name}']
    if region:
        keys.insert(0, f'{code}|{region["code"]}')
    return keys


def get_cities(country_code: str, region_name: str) -> list[str]:
    index = _cities_by_region()
    for key in _city_lookup_keys(country_code, region_name):
        cities = index.get(key)
        if cities:
            return list(cities)
    return []


def city_choices(country_code: str, region_name: str, blank_label='Select city / town'):
    cities = get_cities(country_code, region_name)
    if not cities:
        return []
    return [('', blank_label)] + [(name, name) for name in cities]


def compose_location(city: str, region: str, country_code: str) -> str:
    city = (city or '').strip()
    region = (region or '').strip()
    country = get_country_name(country_code)
    parts = [part for part in (city, region, country) if part]
    return ', '.join(parts)


def format_region_list(regions: list[str]) -> str:
    cleaned = [region.strip() for region in regions if region and region.strip()]
    if not cleaned:
        return ''
    if len(cleaned) == 1:
        return cleaned[0]
    if len(cleaned) == 2:
        return f'{cleaned[0]} & {cleaned[1]}'
    return ', '.join(cleaned[:-1]) + f' & {cleaned[-1]}'


def compose_job_location(
    city: str,
    regions: list[str],
    country_code: str,
    *,
    max_length: int = 120,
) -> str:
    """Build the display location string for single- or multi-region jobs."""
    country = get_country_name(country_code)
    cleaned_regions = [region.strip() for region in regions if region and region.strip()]
    city = (city or '').strip()

    if not cleaned_regions:
        return country[:max_length] if country else ''

    if len(cleaned_regions) == 1:
        return compose_location(city, cleaned_regions[0], country_code)[:max_length]

    region_label = format_region_list(cleaned_regions)
    text = f'{city} · {region_label} · {country}' if city else f'{region_label} · {country}'
    if len(text) <= max_length:
        return text

    if len(cleaned_regions) > 2:
        region_label = f'{cleaned_regions[0]}, {cleaned_regions[1]} +{len(cleaned_regions) - 2} more'
        text = f'{city} · {region_label} · {country}' if city else f'{region_label} · {country}'
    return text[:max_length]


def parse_location(location: str) -> dict[str, str]:
    """
    Best-effort split of stored location text into country / region / city parts.
    Supports formats like 'Lusaka, Lusaka, Zambia', legacy 'Lusaka, Zambia',
    and older listings that stored only 'Remote'.
    """
    raw = (location or '').strip()
    result = {'country': DEFAULT_COUNTRY_CODE, 'region': '', 'city': ''}
    if not raw:
        return result

    lowered = raw.casefold()
    for prefix in ('remote · ', 'remote - ', 'remote, '):
        if lowered.startswith(prefix):
            raw = raw[len(prefix):].strip()
            lowered = raw.casefold()
            break
    if lowered == 'remote':
        return result

    country_by_name = {item['name'].casefold(): item['code'] for item in _countries()}
    parts = [part.strip() for part in raw.split(',') if part.strip()]
    if not parts:
        return result

    if len(parts) >= 2 and parts[-1].casefold() in country_by_name:
        result['country'] = country_by_name[parts[-1].casefold()]
        parts = parts[:-1]

    if len(parts) >= 2:
        result['city'] = parts[0]
        result['region'] = _normalize_region_name(result['country'], parts[1])
    elif len(parts) == 1:
        result['city'] = parts[0]
        regions = get_regions(result['country'])
        match = next((r['name'] for r in regions if r['name'].casefold() == parts[0].casefold()), '')
        if not match:
            match = _normalize_region_name(result['country'], parts[0])
            if not region_valid(result['country'], match):
                match = ''
        if match:
            result['region'] = match
            result['city'] = parts[0]
        else:
            result['city'] = parts[0]

    return result
