"""OpenAPI/Swagger specification crawler."""

from pathlib import Path
from typing import Any
import json

import httpx
import yaml


class OpenAPICrawler:
    """Download and convert OpenAPI specs to markdown."""
    
    def __init__(self, output_dir: Path):
        self.output_dir = Path(output_dir)
    
    def crawl(self, url: str) -> dict:
        """Download OpenAPI spec and convert to markdown."""
        self.output_dir.mkdir(parents=True, exist_ok=True)
        
        # Fetch spec
        with httpx.Client(follow_redirects=True, timeout=30.0) as client:
            response = client.get(url)
            response.raise_for_status()
        
        # Parse spec (JSON or YAML)
        content = response.text
        try:
            spec = json.loads(content)
        except json.JSONDecodeError:
            spec = yaml.safe_load(content)
        
        # Generate markdown files
        pages = []
        
        # Overview
        overview = self._generate_overview(spec)
        overview_path = self.output_dir / "overview.md"
        overview_path.write_text(overview, encoding="utf-8")
        pages.append({"path": str(overview_path), "title": "Overview"})
        
        # Endpoints
        endpoints = self._generate_endpoints(spec)
        endpoints_path = self.output_dir / "endpoints.md"
        endpoints_path.write_text(endpoints, encoding="utf-8")
        pages.append({"path": str(endpoints_path), "title": "Endpoints"})
        
        # Schemas
        schemas = self._generate_schemas(spec)
        if schemas:
            schemas_path = self.output_dir / "schemas.md"
            schemas_path.write_text(schemas, encoding="utf-8")
            pages.append({"path": str(schemas_path), "title": "Schemas"})
        
        # Save original spec
        spec_path = self.output_dir / "openapi.yaml"
        spec_path.write_text(yaml.dump(spec, default_flow_style=False), encoding="utf-8")
        
        return {
            "pages_count": len(pages),
            "output_dir": str(self.output_dir),
            "pages": pages
        }
    
    def _generate_overview(self, spec: dict) -> str:
        """Generate overview markdown from spec."""
        info = spec.get("info", {})
        
        lines = [
            "---",
            f"title: {info.get('title', 'API')} Overview",
            "---",
            "",
            f"# {info.get('title', 'API')}",
            "",
        ]
        
        if desc := info.get("description"):
            lines.extend([desc, ""])
        
        if version := info.get("version"):
            lines.extend([f"**Version:** {version}", ""])
        
        # Servers
        if servers := spec.get("servers", []):
            lines.extend(["## Servers", ""])
            for server in servers:
                desc = server.get("description", "")
                lines.append(f"- `{server['url']}` {desc}")
            lines.append("")
        
        # Authentication
        if security := spec.get("components", {}).get("securitySchemes", {}):
            lines.extend(["## Authentication", ""])
            for name, scheme in security.items():
                scheme_type = scheme.get("type", "")
                lines.append(f"### {name}")
                lines.append(f"- Type: `{scheme_type}`")
                if scheme_type == "http":
                    lines.append(f"- Scheme: `{scheme.get('scheme', '')}`")
                elif scheme_type == "apiKey":
                    lines.append(f"- In: `{scheme.get('in', '')}`")
                    lines.append(f"- Name: `{scheme.get('name', '')}`")
                lines.append("")
        
        return "\n".join(lines)
    
    def _generate_endpoints(self, spec: dict) -> str:
        """Generate endpoints documentation."""
        lines = [
            "---",
            "title: API Endpoints",
            "---",
            "",
            "# API Endpoints",
            "",
        ]
        
        paths = spec.get("paths", {})
        
        # Group by tags
        by_tag: dict[str, list] = {"Other": []}
        
        for path, methods in paths.items():
            for method, details in methods.items():
                if method in ["get", "post", "put", "patch", "delete"]:
                    tags = details.get("tags", ["Other"])
                    for tag in tags:
                        if tag not in by_tag:
                            by_tag[tag] = []
                        by_tag[tag].append({
                            "path": path,
                            "method": method.upper(),
                            "details": details
                        })
        
        for tag, endpoints in sorted(by_tag.items()):
            if not endpoints:
                continue
            
            lines.extend([f"## {tag}", ""])
            
            for ep in endpoints:
                summary = ep["details"].get("summary", ep["path"])
                lines.append(f"### {ep['method']} {ep['path']}")
                lines.append("")
                lines.append(f"**{summary}**")
                lines.append("")
                
                if desc := ep["details"].get("description"):
                    lines.extend([desc, ""])
                
                # Parameters
                if params := ep["details"].get("parameters", []):
                    lines.append("**Parameters:**")
                    lines.append("")
                    lines.append("| Name | In | Type | Required | Description |")
                    lines.append("|------|-----|------|----------|-------------|")
                    for p in params:
                        name = p.get("name", "")
                        in_loc = p.get("in", "")
                        p_type = p.get("schema", {}).get("type", "")
                        required = "Yes" if p.get("required") else "No"
                        desc = p.get("description", "")
                        lines.append(f"| {name} | {in_loc} | {p_type} | {required} | {desc} |")
                    lines.append("")
                
                # Request body
                if body := ep["details"].get("requestBody", {}):
                    lines.append("**Request Body:**")
                    lines.append("")
                    if content := body.get("content", {}):
                        for media_type, schema in content.items():
                            lines.append(f"Content-Type: `{media_type}`")
                            if example := schema.get("example"):
                                lines.append("```json")
                                lines.append(json.dumps(example, indent=2))
                                lines.append("```")
                    lines.append("")
                
                # Responses
                if responses := ep["details"].get("responses", {}):
                    lines.append("**Responses:**")
                    lines.append("")
                    for code, resp in responses.items():
                        desc = resp.get("description", "")
                        lines.append(f"- `{code}`: {desc}")
                    lines.append("")
        
        return "\n".join(lines)
    
    def _generate_schemas(self, spec: dict) -> str:
        """Generate schemas documentation."""
        schemas = spec.get("components", {}).get("schemas", {})
        
        if not schemas:
            return ""
        
        lines = [
            "---",
            "title: Data Schemas",
            "---",
            "",
            "# Data Schemas",
            "",
        ]
        
        for name, schema in schemas.items():
            lines.append(f"## {name}")
            lines.append("")
            
            if desc := schema.get("description"):
                lines.extend([desc, ""])
            
            schema_type = schema.get("type", "object")
            lines.append(f"**Type:** `{schema_type}`")
            lines.append("")
            
            if properties := schema.get("properties", {}):
                required = schema.get("required", [])
                lines.append("**Properties:**")
                lines.append("")
                lines.append("| Property | Type | Required | Description |")
                lines.append("|----------|------|----------|-------------|")
                for prop_name, prop in properties.items():
                    prop_type = prop.get("type", prop.get("$ref", "any"))
                    is_required = "Yes" if prop_name in required else "No"
                    desc = prop.get("description", "")
                    lines.append(f"| {prop_name} | {prop_type} | {is_required} | {desc} |")
                lines.append("")
            
            if example := schema.get("example"):
                lines.append("**Example:**")
                lines.append("```json")
                lines.append(json.dumps(example, indent=2))
                lines.append("```")
                lines.append("")
        
        return "\n".join(lines)
