#!/usr/bin/env python3
import argparse
import http.client
import json
import sys
import time
import urllib.error
import urllib.parse
import urllib.request


def build_url(base: str, query: str, category: str | None, language: str, safe_search: int) -> str:
    params = {
        'q': query,
        'format': 'json',
        'language': language,
        'safesearch': safe_search,
    }
    if category:
        params['categories'] = category
    return base + '?' + urllib.parse.urlencode(params)


def fetch_json(url: str) -> dict:
    last_error = None
    for attempt in range(4):
        try:
            with urllib.request.urlopen(url, timeout=20) as resp:
                return json.load(resp)
        except (urllib.error.URLError, http.client.HTTPException, ConnectionError) as exc:
            last_error = exc
            if attempt == 3:
                raise
            time.sleep(1.2 * (attempt + 1))
    raise last_error or RuntimeError('failed to fetch search results')


def normalize_unresponsive(items: list) -> list[str]:
    out = []
    for item in items:
        if isinstance(item, str):
            out.append(item)
        elif isinstance(item, (list, tuple)):
            out.append('/'.join(str(x) for x in item if x is not None))
        else:
            out.append(str(item))
    return out


def main() -> int:
    parser = argparse.ArgumentParser(description='Query local SearXNG and print normalized results.')
    parser.add_argument('query', nargs='+', help='search query')
    parser.add_argument('-n', '--limit', type=int, default=8, help='max results to print (default: 8)')
    parser.add_argument('-c', '--category', default=None, help='optional SearXNG category (general, news, images, videos, it, repos, packages, ...)')
    parser.add_argument('-l', '--language', default='en-US', help='language code (default: en-US)')
    parser.add_argument('--json', action='store_true', help='print normalized JSON instead of pretty text')
    parser.add_argument('--safe-search', type=int, default=1, choices=[0, 1, 2], help='0=off 1=moderate 2=strict')
    parser.add_argument('--base-url', default='http://127.0.0.1:18080/search', help='SearXNG search endpoint')
    args = parser.parse_args()

    query = ' '.join(args.query).strip()
    if not query:
        parser.error('query is required')

    url = build_url(args.base_url, query, args.category, args.language, args.safe_search)
    data = fetch_json(url)

    results = []
    for item in data.get('results', [])[: max(args.limit, 1)]:
        results.append({
            'title': (item.get('title') or '').replace('\n', ' ').strip(),
            'url': (item.get('url') or '').strip(),
            'content': (item.get('content') or '').replace('\n', ' ').strip(),
            'category': item.get('category'),
            'engine': item.get('engine') or item.get('engines'),
            'publishedDate': item.get('publishedDate'),
        })

    payload = {
        'query': query,
        'category': args.category,
        'language': args.language,
        'resultCount': len(results),
        'unresponsiveEngines': normalize_unresponsive(data.get('unresponsive_engines', [])),
        'results': results,
    }

    if args.json:
        json.dump(payload, sys.stdout, ensure_ascii=False, indent=2)
        sys.stdout.write('\n')
        return 0

    if not results:
        print(f'No results for: {query}')
        if payload['unresponsiveEngines']:
            print('Unresponsive engines: ' + ', '.join(payload['unresponsiveEngines']))
        return 0

    for i, item in enumerate(results, 1):
        print(f"{i}. {item['title']}")
        if item['url']:
            print(f"   {item['url']}")
        meta = []
        if item['category']:
            meta.append(f"category={item['category']}")
        if item['engine']:
            meta.append(f"engine={item['engine']}")
        if item['publishedDate']:
            meta.append(f"published={item['publishedDate']}")
        if meta:
            print('   ' + ' | '.join(str(x) for x in meta))
        if item['content']:
            print(f"   {item['content'][:260]}")

    if payload['unresponsiveEngines']:
        print('\nUnresponsive engines: ' + ', '.join(payload['unresponsiveEngines']))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
