"""registry 기반 자동 문서 생성.

registry.py (단일 진실의 원천)에서 다음 파일을 자동 생성한다:
- src/dartlab/API_SPEC.md  — 개발자용 API 레퍼런스
- landing/static/llms.txt  — AI 크롤러용 구조화 문서
- .claude/skills/dartlab/reference.md — Claude Code 스킬 레퍼런스

실행:
    uv run python scripts/generateSpec.py

릴리즈 시 CI에서 자동 실행하여 수동 관리 포인트를 제거한다.
"""

from __future__ import annotations

import dataclasses
import sys
import textwrap
from pathlib import Path
from typing import Optional, get_type_hints

ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))

from dartlab.core.registry import DataEntry, getCategories, getEntries


def _inspectDataclass(cls: type) -> list[tuple[str, str, str]]:
    """dataclass의 (필드명, 타입, docstring근사) 목록 반환."""
    rows = []
    hints = get_type_hints(cls)
    for f in dataclasses.fields(cls):
        typeName = hints.get(f.name, "")
        if hasattr(typeName, "__name__"):
            typeName = typeName.__name__
        else:
            typeName = str(typeName).replace("typing.", "")
        defaultStr = ""
        if f.default is not dataclasses.MISSING:
            defaultStr = str(f.default)
        elif f.default_factory is not dataclasses.MISSING:
            defaultStr = "[]" if "list" in typeName.lower() else "{}"
        rows.append((f.name, typeName, defaultStr))
    return rows


def _dataclassTable(cls: type, title: str) -> str:
    """dataclass를 마크다운 테이블로."""
    rows = _inspectDataclass(cls)
    lines = [f"### {title}\n"]
    doc = cls.__doc__
    if doc:
        lines.append(f"{doc.strip()}\n")
    lines.append("| 필드 | 타입 | 기본값 |")
    lines.append("|------|------|--------|")
    for name, typ, default in rows:
        lines.append(f"| `{name}` | `{typ}` | {default} |")
    lines.append("")
    return "\n".join(lines)


def _categoryLabel(cat: str) -> str:
    labels = {
        "finance": "시계열 재무제표",
        "report": "공시 파싱 모듈",
        "disclosure": "서술형 공시",
        "notes": "K-IFRS 주석",
        "raw": "원본 데이터",
        "analysis": "분석 엔진",
    }
    return labels.get(cat, cat)


def _entryRow(e: DataEntry) -> str:
    """단일 엔트리를 마크다운 테이블 행으로."""
    return f"| `{e.name}` | {e.label} | `{e.dataType}` | {e.description} |"


def _registrySection() -> str:
    """registry 엔트리를 카테고리별 테이블로."""
    lines = ["## 데이터 레지스트리\n"]
    lines.append("`core/registry.py`에 등록된 전체 데이터 소스 목록.\n")
    lines.append("모듈 추가 = registry에 DataEntry 한 줄 추가 → Company, Excel, LLM, Server, Skills 전부 자동 반영.\n")

    for cat in getCategories():
        entries = getEntries(category=cat)
        lines.append(f"### {_categoryLabel(cat)} ({cat})\n")
        lines.append("| name | label | dataType | description |")
        lines.append("|------|-------|----------|-------------|")
        for e in entries:
            lines.append(_entryRow(e))
        lines.append("")
    return "\n".join(lines)


def _dataclassesSection() -> str:
    """주요 dataclass 스키마."""
    lines = ["## 주요 데이터 타입\n"]

    from dartlab.engines.common.finance.ratios import RatioResult
    lines.append(_dataclassTable(RatioResult, "RatioResult"))

    from dartlab.engines.insight.types import AnalysisResult, Anomaly, Flag, InsightResult
    lines.append(_dataclassTable(InsightResult, "InsightResult"))
    lines.append(_dataclassTable(Anomaly, "Anomaly"))
    lines.append(_dataclassTable(Flag, "Flag"))
    lines.append(_dataclassTable(AnalysisResult, "AnalysisResult"))

    from dartlab.engines.sector.types import SectorInfo, SectorParams
    lines.append(_dataclassTable(SectorInfo, "SectorInfo"))
    lines.append(_dataclassTable(SectorParams, "SectorParams"))

    from dartlab.engines.rank.rank import RankInfo
    lines.append(_dataclassTable(RankInfo, "RankInfo"))

    return "\n".join(lines)


def _companySection() -> str:
    """Company 팩토리 + KRCompany/USCompany/Compare 사용법."""
    return textwrap.dedent("""\
    ## Company (팩토리)

    입력을 자동 판별하여 KRCompany 또는 USCompany를 생성한다.

    ```python
    from dartlab import Company, Compare

    kr = Company("005930")       # → KRCompany (DART 종목코드)
    kr = Company("삼성전자")      # → KRCompany (회사명)
    us = Company("AAPL")         # → USCompany (SEC ticker)

    kr.market                    # "KR"
    us.market                    # "US"
    ```

    ### 판별 규칙

    | 입력 | 결과 | 예시 |
    |------|------|------|
    | 6자리 숫자 | KRCompany | `Company("005930")` |
    | 한글 포함 | KRCompany | `Company("삼성전자")` |
    | 영문 1~5자리 | USCompany | `Company("AAPL")` |

    ## KRCompany (DART 한국 기업)

    ### 정적 메서드

    | 메서드 | 반환 | 설명 |
    |--------|------|------|
    | `KRCompany.listing()` | DataFrame | KRX 전체 상장법인 목록 |
    | `KRCompany.search(keyword)` | DataFrame | 회사명 부분 검색 |
    | `KRCompany.status()` | DataFrame | 로컬 보유 전체 종목 인덱스 |
    | `KRCompany.resolve(codeOrName)` | str \\| None | 종목코드/회사명 → 종목코드 |

    ### 핵심 property

    | property | 반환 | 설명 |
    |----------|------|------|
    | `BS` | DataFrame | 재무상태표 |
    | `IS` | DataFrame | 손익계산서 |
    | `CF` | DataFrame | 현금흐름표 |
    | `timeseries` | (series, periods) | 분기별 standalone 시계열 |
    | `annual` | (series, years) | 연도별 시계열 |
    | `ratios` | RatioResult | 재무비율 |
    | `sector` | SectorInfo | 섹터 분류 |
    | `insights` | AnalysisResult | 7영역 인사이트 등급 |
    | `rank` | RankInfo | 시장 순위 |
    | `notes` | Notes | K-IFRS 주석 접근 |
    | `market` | str | `"KR"` |

    ### 메서드

    | 메서드 | 반환 | 설명 |
    |--------|------|------|
    | `get(name)` | Result | 모듈 전체 Result 객체 |
    | `all()` | dict | 전체 데이터 dict |
    | `fsSummary(period)` | AnalysisResult | 요약재무정보 |
    | `getTimeseries(period, fsDivPref)` | (series, periods) | 커스텀 시계열 |
    | `getRatios(fsDivPref)` | RatioResult | 커스텀 비율 |

    report/disclosure property는 registry에서 자동 디스패치된다 (`_MODULE_REGISTRY`).
    등록된 모든 property는 아래 "데이터 레지스트리" 섹션 참조.

    ## USCompany (EDGAR 미국 기업)

    ```python
    us = Company("AAPL")
    us.ticker                    # "AAPL"
    us.cik                       # "0000320193"
    ```

    ### property

    | property | 반환 | 설명 |
    |----------|------|------|
    | `timeseries` | (series, periods) | 분기별 standalone 시계열 |
    | `annual` | (series, years) | 연도별 시계열 |
    | `ratios` | RatioResult | 재무비율 |
    | `insights` | AnalysisResult | 7영역 인사이트 등급 |
    | `market` | str | `"US"` |

    ## Compare (복수 기업 비교)

    ```python
    c = Compare(Company("005930"), Company("AAPL"))
    ```

    ### 재무비율 비교

    | Property | 설명 |
    |----------|------|
    | `ratios` | 전체 비율 (30+ 비율) |
    | `profitability` | 수익성 (ROE, ROA, 마진 등) |
    | `stability` | 안정성 (부채비율, 유동비율 등) |
    | `growth` | 성장성 (매출/이익 YoY, CAGR) |
    | `efficiency` | 효율성 (회전율) |
    | `cashflowRatios` | 현금흐름 (FCF, CF마진 등) |
    | `valuation` | 밸류에이션 (PER, PBR 등) |
    | `scale` | 규모 절대값 (매출, 자산, 시총) |

    ### 인사이트 비교

    | Property | 설명 |
    |----------|------|
    | `insights` | 7영역 등급 비교 |
    | `insightDetails` | 등급 + 요약 상세 비교 |

    ### 연도별 시계열 비교

    | Property | 설명 |
    |----------|------|
    | `revenue` | 매출 |
    | `operatingProfit` | 영업이익 |
    | `netIncome` | 순이익 |
    | `totalAssets` | 총자산 |
    | `equity` | 자본(지배기업) |
    | `operatingCashflow` | 영업활동CF |

    ### 비율 추이 비교

    | Property | 설명 |
    |----------|------|
    | `roeHistory` | ROE 연도별 추이 |
    | `debtRatioHistory` | 부채비율 추이 |
    | `marginHistory` | 영업이익률 추이 |
    | `netMarginHistory` | 순이익률 추이 |
    | `currentRatioHistory` | 유동비율 추이 |
    | `ratioHistory("snakeId")` | 임의 비율 추이 |
    """)


def _header(title: str, description: str) -> str:
    return f"# {title}\n\n{description}\n\n"


def generateApiSpec() -> str:
    """API_SPEC.md 생성."""
    parts = [
        _header(
            "dartlab API 스펙",
            "이 문서는 `scripts/generateSpec.py`에 의해 자동 생성됩니다. 직접 수정하지 마세요."
        ),
        _companySection(),
        _registrySection(),
        _dataclassesSection(),
    ]
    return "\n---\n\n".join(parts)


def generateLlmsTxt() -> str:
    """llms.txt 생성 — AI 크롤러용."""
    lines = [
        "# dartlab",
        "",
        "> DART 전자공시 데이터를 활용한 한국 상장기업 재무 분석 Python 라이브러리.",
        "",
        "## 설치",
        "",
        "```",
        "pip install dartlab",
        "```",
        "",
        "## 빠른 시작",
        "",
        "```python",
        "from dartlab import Company",
        "",
        'c = Company("005930")  # 삼성전자',
        "c.BS                   # 재무상태표",
        "c.ratios               # 재무비율 (ROE, PER 등)",
        "c.insights             # 7영역 인사이트 등급 (A~F)",
        "```",
        "",
        "## 주요 기능",
        "",
    ]

    for cat in getCategories():
        entries = getEntries(category=cat)
        lines.append(f"### {_categoryLabel(cat)}")
        lines.append("")
        for e in entries:
            lines.append(f"- **{e.name}** ({e.label}): {e.description}")
        lines.append("")

    lines.extend([
        "## 분석 엔진",
        "",
        "- **섹터 분류**: WICS 11섹터 자동 분류 (오버라이드 → 키워드 → KSIC 3단계)",
        "- **인사이트 등급**: 7영역 A~F 등급 (실적, 수익성, 건전성, 현금흐름, 지배구조, 리스크, 기회)",
        "- **시장 순위**: 매출/자산/성장률 전체+섹터내 순위",
        "- **재무비율**: ROE, ROA, 영업이익률, 부채비율, PER, PBR, FCF 등 자동 계산",
        "",
        "## 링크",
        "",
        "- 문서: https://eddmpython.github.io/dartlab/docs/",
        "- GitHub: https://github.com/eddmpython/dartlab",
        "- PyPI: https://pypi.org/project/dartlab/",
        "",
    ])

    return "\n".join(lines)


def generateSkillRef() -> str:
    """Claude Code 스킬용 reference.md 생성."""
    parts = [
        _header(
            "dartlab API Reference (Skills용)",
            "이 문서는 `scripts/generateSpec.py`에 의해 자동 생성됩니다."
        ),
        _companySection(),
        _registrySection(),
        _dataclassesSection(),
    ]
    return "\n---\n\n".join(parts)


def main():
    apiSpecPath = ROOT / "src" / "dartlab" / "API_SPEC.md"
    llmsTxtPath = ROOT / "landing" / "static" / "llms.txt"
    skillRefPath = ROOT / ".claude" / "skills" / "dartlab" / "reference.md"

    skillRefPath.parent.mkdir(parents=True, exist_ok=True)

    apiSpec = generateApiSpec()
    apiSpecPath.write_text(apiSpec, encoding="utf-8")
    print(f"  API_SPEC.md  ({len(apiSpec):,} chars) → {apiSpecPath}")

    llmsTxt = generateLlmsTxt()
    llmsTxtPath.write_text(llmsTxt, encoding="utf-8")
    print(f"  llms.txt     ({len(llmsTxt):,} chars) → {llmsTxtPath}")

    skillRef = generateSkillRef()
    skillRefPath.write_text(skillRef, encoding="utf-8")
    print(f"  reference.md ({len(skillRef):,} chars) → {skillRefPath}")

    print("\n  완료.")


if __name__ == "__main__":
    main()
