#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import sys
from dataclasses import asdict, dataclass
from typing import Any, Optional

import httpx


USER_AGENT = (
    "kyotool-fetch/0.1 (+https://example.local) "
    "Python-httpx agent-friendly fetch tool"
)

DEFAULT_TIMEOUT = 20.0


@dataclass
class FetchResult:
    ok: bool
    url: str
    final_url: str
    status_code: int
    content_type: str
    title: str
    byline: Optional[str]
    excerpt: Optional[str]
    format: str
    content: str
    text_length: int
    links: list[dict[str, str]]
    metadata: dict[str, Any]
    error: Optional[str] = None


def fetch_url(
    url: str,
    output_format: str,
    timeout: float,
    include_headers: bool,
) -> FetchResult:
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.8,ko;q=0.7",
    }

    try:
        with httpx.Client(
            follow_redirects=True,
            timeout=timeout,
            headers=headers,
        ) as client:
            response = client.get(url)

        content_type = response.headers.get("content-type", "")
        final_url = str(response.url)

        if response.status_code >= 400:
            return FetchResult(
                ok=False,
                url=url,
                final_url=final_url,
                status_code=response.status_code,
                content_type=content_type,
                title="",
                byline=None,
                excerpt=None,
                format=output_format,
                content="",
                text_length=0,
                links=[],
                metadata={},
                error=f"HTTP {response.status_code}",
            )

        metadata: dict[str, Any] = {}
        if include_headers:
            metadata["response_headers"] = dict(response.headers)

        raw_content = response.text
        return FetchResult(
            ok=True,
            url=url,
            final_url=final_url,
            status_code=response.status_code,
            content_type=content_type,
            title="",
            byline=None,
            excerpt=None,
            format=output_format,
            content=raw_content,
            text_length=len(raw_content),
            links=[],
            metadata=metadata,
            error=None,
        )

    except httpx.TimeoutException:
        return FetchResult(
            ok=False,
            url=url,
            final_url=url,
            status_code=0,
            content_type="",
            title="",
            byline=None,
            excerpt=None,
            format=output_format,
            content="",
            text_length=0,
            links=[],
            metadata={},
            error="request timed out",
        )
    except Exception as exc:
        return FetchResult(
            ok=False,
            url=url,
            final_url=url,
            status_code=0,
            content_type="",
            title="",
            byline=None,
            excerpt=None,
            format=output_format,
            content="",
            text_length=0,
            links=[],
            metadata={},
            error=str(exc),
        )


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="kyotool-fetch",
        description="Agent-friendly web fetch CLI",
    )
    parser.add_argument("url", help="URL to fetch")
    parser.add_argument(
        "--format",
        choices=["markdown", "text", "html"],
        default="markdown",
        help="Output content format",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="Emit structured JSON result",
    )
    parser.add_argument(
        "--timeout",
        type=float,
        default=DEFAULT_TIMEOUT,
        help="Request timeout in seconds",
    )
    parser.add_argument(
        "--include-headers",
        action="store_true",
        help="Include response headers in metadata",
    )
    parser.add_argument(
        "--links-only",
        action="store_true",
        help="Only emit extracted links",
    )
    return parser


def _links_only_payload(result: FetchResult) -> dict[str, Any]:
    return {
        "ok": result.ok,
        "url": result.url,
        "final_url": result.final_url,
        "status_code": result.status_code,
        "title": result.title,
        "links": result.links,
        "error": result.error,
    }


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()

    result = fetch_url(
        url=args.url,
        output_format=args.format,
        timeout=args.timeout,
        include_headers=args.include_headers,
    )

    if args.links_only:
        print(json.dumps(_links_only_payload(result), ensure_ascii=False, indent=2))
        return 0 if result.ok else 1

    if args.json:
        print(json.dumps(asdict(result), ensure_ascii=False, indent=2))
        return 0 if result.ok else 1

    if not result.ok:
        print(f"ERROR: {result.error}", file=sys.stderr)
        return 1

    print(result.content)
    return 0


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