#!/usr/bin/env python3
"""시크릿 로테이션 체크 스크립트.

인벤토리 JSON을 읽어 각 시크릿의 만료 상태를 확인하고 보고한다.
"""

import argparse
import json
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import TypedDict

_WORKSPACE_ROOT = os.environ.get("WORKSPACE_ROOT", str(Path(__file__).resolve().parent.parent))
DEFAULT_INVENTORY = str(Path(_WORKSPACE_ROOT) / "memory" / "security" / "secret-inventory.json")
WARNING_DAYS = 14


class SecretEntry(TypedDict):
    name: str
    source_file: str
    owner: str
    created_date: str
    last_rotated: str
    expires_date: str
    category: str
    rotation_notes: str


class CheckResult(TypedDict):
    name: str
    status: str
    expires_date: str
    days_remaining: int


class Summary(TypedDict):
    total: int
    ok: int
    warning: int
    expired: int


class JsonOutput(TypedDict):
    results: list[CheckResult]
    summary: Summary


def check_secret(secret: SecretEntry, today: datetime) -> CheckResult:
    """단일 시크릿의 만료 상태를 계산하여 반환한다."""
    expires = datetime.strptime(secret["expires_date"], "%Y-%m-%d")
    days_remaining = (expires - today).days

    if days_remaining < 0:
        status = "EXPIRED"
    elif days_remaining <= WARNING_DAYS:
        status = "WARNING"
    else:
        status = "OK"

    return {
        "name": secret["name"],
        "status": status,
        "expires_date": secret["expires_date"],
        "days_remaining": days_remaining,
    }


def load_inventory(inventory_path: str) -> dict:  # type: ignore[type-arg]
    """인벤토리 JSON 파일을 로드하여 반환한다."""
    with open(inventory_path, "r", encoding="utf-8") as f:
        return json.load(f)  # type: ignore[no-any-return]


def update_last_checked(inventory_path: str, inventory: dict) -> None:  # type: ignore[type-arg]
    """last_checked 타임스탬프를 현재 시각으로 갱신하고 파일에 저장한다."""
    inventory["last_checked"] = datetime.now().isoformat()
    with open(inventory_path, "w", encoding="utf-8") as f:
        json.dump(inventory, f, indent=2, ensure_ascii=False)


def print_text_report(results: list[CheckResult]) -> None:
    """텍스트 형식으로 결과를 stdout에 출력한다."""
    for r in results:
        days = r["days_remaining"]
        if r["status"] == "EXPIRED":
            print(f"[EXPIRED] {r['name']} - expires_date: {r['expires_date']} (overdue {abs(days)}d)")
        elif r["status"] == "WARNING":
            print(f"[WARNING] {r['name']} - expires_date: {r['expires_date']} ({days}d remaining)")
        else:
            print(f"[OK]      {r['name']} - expires_date: {r['expires_date']} ({days}d remaining)")


def build_json_output(results: list[CheckResult]) -> JsonOutput:
    """JSON 출력 구조를 생성하여 반환한다."""
    ok_count = sum(1 for r in results if r["status"] == "OK")
    warning_count = sum(1 for r in results if r["status"] == "WARNING")
    expired_count = sum(1 for r in results if r["status"] == "EXPIRED")

    return {
        "results": results,
        "summary": {
            "total": len(results),
            "ok": ok_count,
            "warning": warning_count,
            "expired": expired_count,
        },
    }


def parse_args() -> argparse.Namespace:
    """CLI 인자를 파싱하여 반환한다."""
    parser = argparse.ArgumentParser(description="시크릿 로테이션 상태를 확인한다.")
    parser.add_argument(
        "--inventory",
        default=DEFAULT_INVENTORY,
        help="인벤토리 JSON 파일 경로 (기본값: %(default)s)",
    )
    parser.add_argument(
        "--json",
        dest="json_output",
        action="store_true",
        help="결과를 JSON 형식으로 출력한다",
    )
    parser.add_argument(
        "--update-checked",
        action="store_true",
        help="last_checked 타임스탬프를 현재 시각으로 갱신한다",
    )
    return parser.parse_args()


def main() -> None:
    """메인 진입점."""
    args = parse_args()

    inventory = load_inventory(args.inventory)
    secrets: list[SecretEntry] = inventory.get("secrets", [])
    today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)

    results: list[CheckResult] = [check_secret(s, today) for s in secrets]

    if args.json_output:
        output = build_json_output(results)
        print(json.dumps(output, ensure_ascii=False))
    else:
        print_text_report(results)

    if args.update_checked:
        update_last_checked(args.inventory, inventory)

    has_issue = any(r["status"] in ("EXPIRED", "WARNING") for r in results)
    sys.exit(1 if has_issue else 0)


if __name__ == "__main__":
    main()
