"""OpenAlex 搜索命令行工具。"""

from __future__ import annotations

import argparse
import json
import sys
from typing import Any

try:
    import requests
except ImportError:
    print("警告: 未安装 requests，已跳过 OpenAlex CLI。", file=sys.stderr)
    sys.exit(0)


API_URL = "https://api.openalex.org/works"
REQUEST_TIMEOUT_SECONDS = 30.0
USER_AGENT = "openalex_fetch.py/1.0"


def _clean_text(value: Any) -> str:
    """压缩空白并清理文本值。"""
    return " ".join(str(value).split())


def _normalize_year_filter(raw_year: str) -> str:
    """将命令行年份参数转换为 OpenAlex filter 语法。"""
    value = raw_year.strip()
    if not value:
        raise ValueError("年份参数不能为空")

    if value.isdigit() and len(value) == 4:
        return f"publication_year:{value}"

    if value.endswith("-") and value[:-1].strip().isdigit():
        start_year = value[:-1].strip()
        return f"from_publication_date:{start_year}-01-01"

    if value.startswith("-") and value[1:].strip().isdigit():
        end_year = value[1:].strip()
        return f"to_publication_date:{end_year}-12-31"

    if "-" in value:
        start_year, end_year = [part.strip() for part in value.split("-", 1)]
        if start_year.isdigit() and end_year.isdigit():
            return f"publication_year:{start_year}-{end_year}"

    raise ValueError(f"无法识别的年份参数: {raw_year}")


def _normalize_sort_value(sort_value: str) -> str:
    """将用户输入的排序参数转换为 OpenAlex sort 语法。"""
    value = sort_value.strip()
    if not value:
        raise ValueError("排序参数不能为空")
    if value in {"relevance", "relevance_score"}:
        return "relevance_score:desc"
    return value


def _normalize_work(work: dict[str, Any]) -> dict[str, Any]:
    """将 OpenAlex work 记录压缩成稳定输出结构。"""
    authorships = work.get("authorships")
    authors: list[str] = []
    institutions: list[str] = []
    if isinstance(authorships, list):
        for authorship in authorships:
            if not isinstance(authorship, dict):
                continue
            author = authorship.get("author")
            if isinstance(author, dict):
                display_name = author.get("display_name")
                if display_name:
                    cleaned_name = _clean_text(display_name)
                    if cleaned_name not in authors:
                        authors.append(cleaned_name)
            author_institutions = authorship.get("institutions")
            if isinstance(author_institutions, list):
                for institution in author_institutions:
                    if not isinstance(institution, dict):
                        continue
                    display_name = institution.get("display_name")
                    if display_name:
                        cleaned_name = _clean_text(display_name)
                        if cleaned_name not in institutions:
                            institutions.append(cleaned_name)

    return {
        "title": _clean_text(work.get("display_name", "")),
        "authors": authors,
        "year": work.get("publication_year"),
        "cited_by_count": work.get("cited_by_count"),
        "institutions": institutions,
        "doi": work.get("doi"),
    }


def search_openalex(
    query: str,
    max_results: int,
    year: str | None,
    work_type: str | None,
    sort: str,
) -> list[dict[str, Any]]:
    """搜索 OpenAlex 并返回标准化论文结果。"""
    params: dict[str, Any] = {
        "search": query,
        "per-page": max_results,
        "sort": _normalize_sort_value(sort),
    }

    filters: list[str] = []
    if year:
        filters.append(_normalize_year_filter(year))
    if work_type:
        filters.append(f"type:{work_type.strip()}")
    if filters:
        params["filter"] = ",".join(filters)

    response = requests.get(
        API_URL,
        params=params,
        headers={"User-Agent": USER_AGENT},
        timeout=REQUEST_TIMEOUT_SECONDS,
    )
    response.raise_for_status()

    payload = response.json()
    if not isinstance(payload, dict):
        raise ValueError("OpenAlex 返回了非对象类型的 JSON")

    works = payload.get("results", [])
    if not isinstance(works, list):
        raise ValueError("OpenAlex 响应缺少 results 列表")

    normalized_results: list[dict[str, Any]] = []
    for work in works:
        if isinstance(work, dict):
            normalized_results.append(_normalize_work(work))
    return normalized_results


def build_parser() -> argparse.ArgumentParser:
    """构建命令行参数解析器。"""
    parser = argparse.ArgumentParser(description="OpenAlex 搜索工具")
    subparsers = parser.add_subparsers(dest="command", required=True)

    search_parser = subparsers.add_parser("search", help="搜索 OpenAlex works")
    search_parser.add_argument("query", help="搜索关键词")
    search_parser.add_argument("--max", dest="max_results", type=int, default=10)
    search_parser.add_argument("--year", default=None, help="年份或年份范围")
    search_parser.add_argument(
        "--type", dest="work_type", default=None, help="作品类型"
    )
    search_parser.add_argument(
        "--sort",
        default="relevance",
        help="排序字段，relevance 会映射为 relevance_score:desc",
    )
    return parser


def main(argv: list[str] | None = None) -> int:
    """CLI 入口。"""
    parser = build_parser()
    args = parser.parse_args(argv)

    try:
        if args.command == "search":
            results = search_openalex(
                query=args.query,
                max_results=args.max_results,
                year=args.year,
                work_type=args.work_type,
                sort=args.sort,
            )
            json.dump(results, sys.stdout, ensure_ascii=False, indent=2)
            sys.stdout.write("\n")
            return 0
    except (requests.RequestException, ValueError) as exc:
        print(f"错误: {exc}", file=sys.stderr)
        return 1

    parser.error(f"未知命令: {args.command}")
    return 2


if __name__ == "__main__":
    sys.exit(main())
