import logging
import json
import os

import requests
import tempfile
import stat

from myst_connector.settings import get_app_int, get_path, get_url


logger = logging.getLogger(__name__)


MAX_RETRIES = get_app_int('max_retries')
PROPOSALS_BASE_URL = get_url('proposals_base_url')
PROPOSALS_FILE = get_path('proposals_file')
PROPOSALS_PARAM_FILE = get_path('proposals_param_file')


def get_proposals_params(minimum_provider_quality, country=None):
    return {
        'minimum_provider_quality': minimum_provider_quality,
        'country': country,
    }


def read_proposals_params():
    try:
        with open(PROPOSALS_PARAM_FILE, 'r') as file:
            return json.loads(file.read())
    except Exception:
        return None


def write_proposals_params(params):
    os.makedirs(os.path.dirname(PROPOSALS_PARAM_FILE), exist_ok=True)
    # write atomically
    dirpath = os.path.dirname(PROPOSALS_PARAM_FILE)
    fd, tmp_path = tempfile.mkstemp(dir=dirpath)
    try:
        with os.fdopen(fd, 'w') as f:
            json.dump(params, f)
            f.flush()
            os.fsync(f.fileno())
        os.replace(tmp_path, PROPOSALS_PARAM_FILE)
        os.chmod(PROPOSALS_PARAM_FILE, 0o600)
    finally:
        if os.path.exists(tmp_path):
            try:
                os.remove(tmp_path)
            except Exception as e:
                logger.warning('Failed to remove temporary file: %s', e)


def proposals_cache_is_current(params):
    if not os.path.isfile(PROPOSALS_FILE) or not os.path.isfile(PROPOSALS_PARAM_FILE):
        return False

    return read_proposals_params() == params


def build_proposals_url(minimum_provider_quality, country=None):
    url = f'{PROPOSALS_BASE_URL}&quality_min={minimum_provider_quality}'
    if country:
        url += f'&location_country={country}'
    return url


def generate_proposals_file(minimum_provider_quality, country=None):
    params = get_proposals_params(minimum_provider_quality, country)
    try:
        if proposals_cache_is_current(params):
            logger.info('Existing proposals file found: %s', PROPOSALS_FILE)
            return

        os.makedirs(os.path.dirname(PROPOSALS_FILE), exist_ok=True)
        logger.info('Fetching proposals with minimum quality: %s', minimum_provider_quality)
        if country:
            logger.info('Filtering proposals for country: %s', country)
        try:
            resp = requests.get(build_proposals_url(minimum_provider_quality, country), timeout=10)
            resp.raise_for_status()
            body = resp.text.strip()
        except Exception as e:
            logger.error('Fetching proposals failed: %s', e)
            raise

        # write proposals atomically and with safe permissions
        dirpath = os.path.dirname(PROPOSALS_FILE)
        fd, tmp_path = tempfile.mkstemp(dir=dirpath)
        try:
            with os.fdopen(fd, 'w') as f:
                f.write(body)
                f.flush()
                os.fsync(f.fileno())
            os.replace(tmp_path, PROPOSALS_FILE)
            os.chmod(PROPOSALS_FILE, 0o600)
        finally:
            if os.path.exists(tmp_path):
                try:
                    os.remove(tmp_path)
                except Exception as e:
                    logger.warning('Failed to remove temporary file: %s', e)
        write_proposals_params(params)
    except Exception:
        logger.error('Fetching proposals failed')
        raise


def read_proposals_file():
    try:
        logger.info('Parsing proposals from %s', os.path.basename(PROPOSALS_FILE))
        with open(PROPOSALS_FILE, 'r') as file:
            data = json.loads(file.read())
        if not isinstance(data, list):
            raise ValueError('Proposals file JSON is not a list')
        providers = []
        for proposal in data:
            pid = proposal.get('provider_id')
            if not pid or not isinstance(pid, str):
                continue
            providers.append(pid)
        return providers
    except Exception:
        logger.error('Parsing proposals failed')
        raise


def load_providers(minimum_provider_quality, country=None):
    for attempt in range(MAX_RETRIES):
        logger.info('Provider load attempt %d of %d', attempt + 1, MAX_RETRIES)
        generate_proposals_file(minimum_provider_quality, country)
        try:
            providers = read_proposals_file()
            if not providers:
                logger.warning('No providers available for selected filters')
                return []
            return providers
        except Exception:
            try:
                os.remove(PROPOSALS_FILE)
            except FileNotFoundError:
                pass
            if attempt == MAX_RETRIES - 1:
                logger.error('Failed to load providers after %d attempts', MAX_RETRIES)
                raise SystemExit(1)
            logger.info('Retrying proposal generation')
