import json

import requests as http
from flask import Blueprint, Response, current_app, jsonify, stream_with_context
from flask_login import login_required

from app import db as database

sync_bp = Blueprint('sync', __name__)


# ── Shopify token management ────────────────────────────────────────────────

def _normalize_shop_domain(domain: str) -> str:
    domain = (domain or '').strip()
    for prefix in ('https://', 'http://'):
        if domain.startswith(prefix):
            domain = domain[len(prefix):]
    domain = domain.rstrip('/')
    if '.' not in domain:
        domain = f'{domain}.myshopify.com'
    return domain


@sync_bp.route('/shopify/request-token', methods=['POST'])
@login_required
def shopify_request_token():
    """Request a new Shopify access token using client credentials and save it."""
    cfg = current_app.config
    shop_domain   = _normalize_shop_domain(cfg.get('SHOPIFY_SHOP_DOMAIN') or cfg.get('SHOPIFY_DOMAIN', ''))
    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]):
        return jsonify(ok=False, error='Shop Domain, Client ID and Client Secret must be set first.'), 400

    url = f'https://{shop_domain}/admin/oauth/access_token'
    try:
        resp = http.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,
        )
    except Exception as e:
        return jsonify(ok=False, error=f'Request failed: {e}'), 502

    if not resp.ok:
        return jsonify(ok=False, error=f'Shopify returned {resp.status_code}: {resp.text[:200]}'), 502

    try:
        token = resp.json().get('access_token', '').strip()
    except ValueError:
        return jsonify(ok=False, error='Non-JSON response from Shopify'), 502

    if not token:
        return jsonify(ok=False, error='Shopify response did not include an access_token'), 502

    # Persist to DB and live config
    db_path = cfg['DATABASE_PATH']
    database.set_setting('SHOPIFY_ACCESS_TOKEN', token, db_path)
    current_app.config['SHOPIFY_ACCESS_TOKEN'] = token

    return jsonify(ok=True, token_preview=token[:8] + '…')


@sync_bp.route('/shopify/test-connection', methods=['POST'])
@login_required
def shopify_test_connection():
    """Validate the current access token against /shop.json."""
    from app.services.shopify import API_VERSION
    cfg = current_app.config
    shop_domain  = _normalize_shop_domain(cfg.get('SHOPIFY_DOMAIN', '') or cfg.get('SHOPIFY_SHOP_DOMAIN', ''))
    access_token = cfg.get('SHOPIFY_ACCESS_TOKEN', '')

    if not shop_domain or not access_token:
        return jsonify(ok=False, error='Shop Domain and Access Token must be set.'), 400

    url = f'https://{shop_domain}/admin/api/{API_VERSION}/shop.json'
    try:
        resp = http.get(
            url,
            headers={'X-Shopify-Access-Token': access_token, 'Accept': 'application/json'},
            timeout=15,
        )
    except Exception as e:
        return jsonify(ok=False, error=f'Request failed: {e}'), 502

    if resp.status_code == 200:
        shop_name = resp.json().get('shop', {}).get('name', shop_domain)
        return jsonify(ok=True, shop_name=shop_name)

    return jsonify(ok=False, error=f'Shopify returned {resp.status_code}'), 401


@sync_bp.route('/sync/shopify', methods=['POST'])
@login_required
def sync_shopify():
    """SSE stream: fetch all Shopify orders and link their IDs to local orders."""
    from app.services import sync as sync_svc
    db_path = current_app.config['DATABASE_PATH']

    def generate():
        try:
            for msg in sync_svc.sync_shopify_ids(db_path):
                yield f"data: {json.dumps({'message': msg})}\n\n"
            yield f"data: {json.dumps({'done': True})}\n\n"
        except Exception as exc:
            yield f"data: {json.dumps({'message': f'ERROR: {exc}', 'done': True})}\n\n"

    return Response(
        stream_with_context(generate()),
        mimetype='text/event-stream',
        headers={'X-Accel-Buffering': 'no', 'Cache-Control': 'no-cache'},
    )


@sync_bp.route('/sync/mappings', methods=['GET'])
@login_required
def get_mappings():
    """Return all non-draft orders, including those without a Shopify link."""
    db_path = current_app.config['DATABASE_PATH']
    mappings = database.get_sync_mappings(db_path)
    return jsonify(mappings=mappings)
