#!/usr/bin/env python3
"""
Build script to preprocess OpenAPI schema at build time instead of runtime.
Only extracts the minimal schema components needed by the application.
"""

import yaml
from pathlib import Path
from typing import Any, Dict, Set, Optional


# Schema names that are actually used by the application
REQUIRED_SCHEMAS = {
    "MarketTrade",
    "MarketCandle",
    "MarketLiquidation",
    "MarketOrderBook",
    "MarketQuote",
    "MarketFundingRate",
    "MarketFundingRatePredicted",
    "MarketOpenInterest",
    "MarketContractPrices",
    "MarketImpliedVolatility",
    "MarketGreeks",
    "PairCandle",
    "IndexCandle",
    "BlockchainBalanceUpdateV2",
    "BlockchainBlockInfoV2",
    "BlockchainFullSingleTransactionResponseV2",
    "BlockchainFullBlockResponseV2",
    "TaxonomyAsset",
    "TaxonomyMetadataAsset",
}


def load_openapi_schema() -> Any:
    """Load and parse the OpenAPI YAML file."""
    openapi_path = Path(__file__).parent.parent / "openapi.yaml"
    if not openapi_path.exists():
        raise FileNotFoundError(f"OpenAPI YAML file not found at {openapi_path}")
    with open(openapi_path) as stream:
        return yaml.safe_load(stream)


def extract_referenced_schemas(schema: Dict[str, Any], all_schemas: Dict[str, Any], visited: Optional[Set[str]] = None) -> Set[str]:
    """Recursively extract all schema names referenced by a given schema."""
    if visited is None:
        visited = set()

    referenced = set()

    def extract_refs(obj: Any) -> None:
        if isinstance(obj, dict):
            if '$ref' in obj:
                ref_path = obj['$ref']
                if ref_path.startswith('#/components/schemas/'):
                    schema_name = ref_path.split('/')[-1]
                    if schema_name not in visited:
                        visited.add(schema_name)
                        referenced.add(schema_name)
                        if schema_name in all_schemas:
                            referenced.update(extract_referenced_schemas(
                                all_schemas[schema_name], all_schemas, visited
                            ))
            else:
                for value in obj.values():
                    extract_refs(value)
        elif isinstance(obj, list):
            for item in obj:
                extract_refs(item)

    extract_refs(schema)
    return referenced


def extract_minimal_schema_components(openapi_data: Dict[str, Any]) -> Dict[str, Any]:
    """Extract only the schema components that are actually used."""
    if 'components' not in openapi_data or 'schemas' not in openapi_data['components']:
        return {"components": {"schemas": {}}}

    all_schemas = openapi_data['components']['schemas']
    minimal_schemas = {}
    all_required = set(REQUIRED_SCHEMAS)

    # Find all schemas referenced by our required schemas
    for schema_name in REQUIRED_SCHEMAS:
        if schema_name in all_schemas:
            minimal_schemas[schema_name] = all_schemas[schema_name]
            # Add any schemas this schema references
            referenced = extract_referenced_schemas(all_schemas[schema_name], all_schemas)
            all_required.update(referenced)

    # Add all referenced schemas to minimal set
    for schema_name in all_required:
        if schema_name in all_schemas:
            minimal_schemas[schema_name] = all_schemas[schema_name]

    return {
        "components": {
            "schemas": minimal_schemas
        }
    }


def generate_schema_constants(minimal_schema_data: Dict[str, Any]) -> str:
    """Generate Python code with the minimal OpenAPI schema as a constant."""

    # Convert to a Python repr string to avoid JSON parsing issues
    schema_repr = repr(minimal_schema_data)

    return f'''"""
Auto-generated minimal schema constants from OpenAPI YAML.
DO NOT EDIT THIS FILE DIRECTLY - it is generated by build.py

This file contains only the schema components needed by the application,
extracted from the full OpenAPI specification for faster loading.
"""

from typing import Any, Dict

# Minimal OpenAPI schema components loaded at build time
OPENAPI_SCHEMA: Dict[str, Any] = {schema_repr}
'''


def main() -> None:
    """Main build function."""
    print("Loading OpenAPI schema...")
    openapi_data = load_openapi_schema()

    print("Extracting minimal schema components...")
    minimal_schema_data = extract_minimal_schema_components(openapi_data)

    original_count = len(openapi_data.get('components', {}).get('schemas', {}))
    minimal_count = len(minimal_schema_data['components']['schemas'])
    print(f"Reduced from {original_count} to {minimal_count} schemas ({minimal_count/original_count*100:.1f}% of original)")

    print("Generating schema constants...")
    schema_code = generate_schema_constants(minimal_schema_data)

    # Write the generated constants to a new file
    output_path = Path(__file__).parent / "_schema_constants.py"
    with open(output_path, 'w') as f:
        f.write(schema_code)

    print(f"Generated minimal schema constants in {output_path}")


if __name__ == "__main__":
    main()
