# -*- coding: utf-8 -*-
"""AI 확산 현황 — 특허·논문·뉴스 수집과 지표 계산.

수집원
  · 특허  : FindIP(MCP 전용) — 노트북에서 직접 받지 못하므로 조회 조건·시각을 기록한 스냅샷을 쓴다(data/inputs/FINDIP_QUERY.md)
  · 논문  : OpenAlex API(연도별 group_by), PubMed E-utilities(연도별 esearch 건수)
  · 뉴스  : Event Registry API(영문 기사 일별 건수, timeAggr), 네이버 뉴스 수집기(시범 표본)
인증키는 환경변수(OPENALEX_API_KEY, EVENTREGISTRY_API_KEY)에서만 읽는다. PubMed는 키가 필요 없다.
"""
from __future__ import annotations

import os
import time

import numpy as np
import pandas as pd
import requests

OPENALEX = "https://api.openalex.org/works"
PUBMED = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
EVENTREGISTRY = "https://eventregistry.org/api/v1/article/getArticles"

# OpenAlex 개념 ID: Artificial intelligence(C154945302)
OPENALEX_QUERIES = {
    "AI_개념_전체": "concepts.id:C154945302",
    "AI_개념_한국": "concepts.id:C154945302,institutions.country_code:KR",
    "생성형AI_LLM": 'title_and_abstract.search:"generative AI"|"large language model"|"generative artificial intelligence"',
}
PUBMED_TERM = '"artificial intelligence"[MeSH Terms] OR "machine learning"[MeSH Terms]'


def openalex_by_year(filt: str, y0: int, y1: int) -> pd.Series:
    """OpenAlex에서 조건에 맞는 문헌 수를 출판연도별로 센다."""
    params = {"filter": f"{filt},publication_year:{y0}-{y1}", "group_by": "publication_year", "per-page": 200}
    key = os.environ.get("OPENALEX_API_KEY")
    if key:
        params["api_key"] = key
    r = requests.get(OPENALEX, params=params, timeout=60)
    r.raise_for_status()
    g = {int(x["key"]): int(x["count"]) for x in r.json()["group_by"]}
    return pd.Series({y: g.get(y, 0) for y in range(y0, y1 + 1)})


def pubmed_by_year(term: str, y0: int, y1: int) -> pd.Series:
    """PubMed E-utilities로 연도별 건수를 센다(출판일 기준)."""
    out = {}
    for y in range(y0, y1 + 1):
        r = requests.get(PUBMED, params={"db": "pubmed", "term": f"({term}) AND {y}[dp]", "retmode": "json", "retmax": 0}, timeout=60)
        r.raise_for_status()
        out[y] = int(r.json()["esearchresult"]["count"])
        time.sleep(0.4)                      # 무키 이용 한도(초당 3회) 준수
    return pd.Series(out)


def eventregistry_daily(keyword: str, lang: str, y0: int, y1: int) -> pd.Series:
    """Event Registry 일별 기사 수(연 단위로 나누어 조회). 조회 1회당 토큰이 소모되므로 연 단위 11회 안팎으로 제한한다."""
    key = os.environ["EVENTREGISTRY_API_KEY"]
    parts = []
    for y in range(y0, y1 + 1):
        q = {"action": "getArticles", "keyword": keyword, "lang": lang, "dateStart": f"{y}-01-01", "dateEnd": f"{y}-12-31",
             "resultType": "timeAggr", "dataType": ["news"], "apiKey": key}
        r = requests.post(EVENTREGISTRY, json=q, timeout=180)
        r.raise_for_status()
        res = r.json().get("timeAggr", {}).get("results", [])
        parts.append(pd.Series({pd.Timestamp(x["date"]): int(x["count"]) for x in res}, dtype="float64"))
    s = pd.concat(parts).sort_index()
    return s[~s.index.duplicated()]


# ── 지표 ───────────────────────────────────────────────────────────────
def growth_multiple(s: pd.Series, a: int, b: int) -> float:
    """b년 값 / a년 값."""
    return float(s.loc[b] / s.loc[a])


def cagr(s: pd.Series, a: int, b: int) -> float:
    return 100.0 * ((s.loc[b] / s.loc[a]) ** (1.0 / (b - a)) - 1.0)


def acceleration(s: pd.Series, split: int, a: int, b: int) -> tuple[float, float]:
    """split년 이전(a~split)과 이후(split~b)의 연평균 증가율."""
    return cagr(s, a, split), cagr(s, split, b)


def to_annual(daily: pd.Series) -> pd.Series:
    """일별 건수 → 연간 합계(관측 일수가 365일에 못 미치는 해는 일평균×365로 환산하지 않고 그대로 둔다)."""
    return daily.groupby(daily.index.year).sum()


# IPC 동시분류 → 산업 대응(설명용 대응표). 한 특허가 여러 분류를 가지므로 건수 합은 총건수보다 크다.
IPC_INDUSTRY = {
    "G06F": "정보통신(컴퓨팅)", "G06Q": "금융·도소매·사업서비스(경영·상거래)", "G06T": "정보통신(영상처리)", "G06V": "정보통신(영상인식)",
    "H04N": "통신·방송(영상통신)", "G16H": "보건(의료정보)", "H04L": "통신(데이터 전송)", "A61B": "보건(진단·의료기기)",
    "G06K": "정보통신(인식·데이터 처리)", "G10L": "정보통신(음성)", "G01N": "화학·정밀(물질 분석)", "H04W": "통신(무선망)",
    "G05B": "기계·제조(공정 제어)", "G08B": "사업서비스(경보·보안)", "G01S": "정밀기기(측위·레이더)", "B60W": "자동차(주행 제어)",
    "G01R": "전기·정밀(전기 측정)", "G05D": "기계(위치·자율 제어)", "G08G": "운수(교통 제어)",
}
