import requests
from flask import current_app

API_VERSION = '2025-10'


def _headers():
    return {
        'X-Shopify-Access-Token': current_app.config['SHOPIFY_ACCESS_TOKEN'],
        'Content-Type': 'application/json',
    }


def _base_url():
    domain = current_app.config['SHOPIFY_DOMAIN']
    return f"https://{domain}/admin/api/{API_VERSION}"


def _refresh_token():
    """Request a new Shopify access token using saved OAuth credentials and persist it."""
    from app import db as database
    cfg = current_app.config

    shop_domain   = cfg.get('SHOPIFY_SHOP_DOMAIN') or cfg.get('SHOPIFY_DOMAIN', '')
    # Strip protocol/trailing slash
    for prefix in ('https://', 'http://'):
        if shop_domain.startswith(prefix):
            shop_domain = shop_domain[len(prefix):]
    shop_domain = shop_domain.rstrip('/')
    if '.' not in shop_domain:
        shop_domain = f'{shop_domain}.myshopify.com'

    client_id     = cfg.get('SHOPIFY_CLIENT_ID', '')
    client_secret = cfg.get('SHOPIFY_CLIENT_SECRET', '')
    grant_type    = cfg.get('SHOPIFY_GRANT_TYPE', 'authorization_code')

    if not all([shop_domain, client_id, client_secret]):
        raise RuntimeError('Cannot refresh Shopify token: Client ID or Client Secret not configured')

    url  = f'https://{shop_domain}/admin/oauth/access_token'
    resp = requests.post(
        url,
        headers={'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
        data={'grant_type': grant_type, 'client_id': client_id, 'client_secret': client_secret},
        timeout=30,
    )
    resp.raise_for_status()
    token = resp.json().get('access_token', '').strip()
    if not token:
        raise RuntimeError('Shopify token refresh returned no access_token')

    db_path = cfg['DATABASE_PATH']
    database.set_setting('SHOPIFY_ACCESS_TOKEN', token, db_path)
    current_app.config['SHOPIFY_ACCESS_TOKEN'] = token
    current_app.logger.info('Shopify access token auto-refreshed successfully')
    return token


def _request(method: str, url: str, **kwargs) -> requests.Response:
    """Make a Shopify API request, auto-refreshing the token once on 401."""
    kwargs.setdefault('headers', _headers())
    resp = getattr(requests, method)(url, **kwargs)
    if resp.status_code == 401:
        current_app.logger.warning('Shopify 401 — refreshing token and retrying')
        _refresh_token()
        kwargs['headers'] = _headers()
        resp = getattr(requests, method)(url, **kwargs)
    resp.raise_for_status()
    return resp


def get_order_id(order_number: str, buyer_name: str = None):
    """Lookup Shopify order by order name (e.g. '#S1001' or 'S1001').

    Returns shopify_id (string) or None.
    If multiple orders match, disambiguates by buyer_name if provided,
    otherwise picks the most recently created one.
    """
    # Shopify order names usually have a '#' prefix
    name = order_number if order_number.startswith('#') else f'#{order_number}'
    url = f"{_base_url()}/orders.json"
    params = {'name': name, 'status': 'any', 'limit': 10}
    resp = _request('get', url, params=params, timeout=15)
    orders = resp.json().get('orders', [])

    if not orders:
        return None

    if len(orders) == 1:
        return str(orders[0]['id'])

    # Disambiguate
    if buyer_name:
        buyer_lower = buyer_name.lower()
        for order in orders:
            shipping = order.get('shipping_address') or {}
            billing = order.get('billing_address') or {}
            candidate = (
                shipping.get('name', '') or
                f"{billing.get('first_name', '')} {billing.get('last_name', '')}".strip()
            ).lower()
            if buyer_lower in candidate or candidate in buyer_lower:
                return str(order['id'])

    # Fallback: most recently created
    orders_sorted = sorted(orders, key=lambda o: o.get('created_at', ''), reverse=True)
    return str(orders_sorted[0]['id'])


def get_fulfillment_orders(shopify_order_id: str) -> list:
    """Return open/in_progress fulfillment orders for a Shopify order."""
    url = f"{_base_url()}/orders/{shopify_order_id}/fulfillment_orders.json"
    resp = _request('get', url, timeout=15)
    fulfillment_orders = resp.json().get('fulfillment_orders', [])
    return [
        fo for fo in fulfillment_orders
        if fo.get('status') in ('open', 'in_progress')
    ]


def fulfill_order(shopify_order_id: str, fulfillment_order_id: str,
                  tracking_number: str, carrier: str) -> dict:
    """Create a fulfillment for a Shopify order with tracking info."""
    url = f"{_base_url()}/fulfillments.json"
    payload = {
        "fulfillment": {
            "line_items_by_fulfillment_order": [
                {"fulfillment_order_id": int(fulfillment_order_id)}
            ],
            "tracking_info": {
                "number": tracking_number,
                "company": carrier,
            },
            "notify_customer": True,
        }
    }
    resp = _request('post', url, json=payload, timeout=15)
    return resp.json().get('fulfillment', {})


def get_shopify_order(order_number: str, buyer_name: str = None) -> dict | None:
    """Return the full Shopify order dict for an e-računi order number, or None."""
    name = order_number if order_number.startswith('#') else f'#{order_number}'
    url = f"{_base_url()}/orders.json"
    params = {'name': name, 'status': 'any', 'limit': 10}
    resp = _request('get', url, params=params, timeout=15)
    orders = resp.json().get('orders', [])

    if not orders:
        return None
    if len(orders) == 1:
        return orders[0]

    if buyer_name:
        buyer_lower = buyer_name.lower()
        for order in orders:
            shipping = order.get('shipping_address') or {}
            billing  = order.get('billing_address')  or {}
            candidate = (
                shipping.get('name', '') or
                f"{billing.get('first_name', '')} {billing.get('last_name', '')}".strip()
            ).lower()
            if buyer_lower in candidate or candidate in buyer_lower:
                return order

    return sorted(orders, key=lambda o: o.get('created_at', ''), reverse=True)[0]


def cancel_order(shopify_order_id: str) -> dict:
    """Cancel a Shopify order."""
    url = f"{_base_url()}/orders/{shopify_order_id}/cancel.json"
    resp = _request('post', url, json={}, timeout=15)
    return resp.json().get('order', {})


def get_all_orders(limit_days: int = 60) -> list:
    """Fetch all Shopify orders from the last `limit_days` days, handling pagination."""
    from datetime import datetime, timedelta, timezone
    since = (datetime.now(timezone.utc) - timedelta(days=limit_days)).strftime('%Y-%m-%dT%H:%M:%SZ')
    url = f"{_base_url()}/orders.json"
    params = {'status': 'any', 'limit': 250, 'created_at_min': since}
    orders = []
    while url:
        resp = _request('get', url, params=params, timeout=30)
        orders.extend(resp.json().get('orders', []))
        # Cursor-based pagination via Link header
        next_url = None
        for part in resp.headers.get('Link', '').split(','):
            if 'rel="next"' in part:
                next_url = part.split(';')[0].strip().strip('<>')
                break
        url = next_url
        params = {}  # next_url already includes query params
    return orders
