from __future__ import annotations
import hashlib, json, re, secrets
from urllib.parse import urlparse
from xml.etree import ElementTree

API_TYPES={'rest','graphql','soap','odata','webhook'}
AUTH_TYPES={'none','api_key','basic','bearer','oauth2_client_credentials','oauth2_authorization_code','hmac','mtls'}
POLICY_TYPES={'rate_limit','retry','timeout','circuit_breaker','ip_allowlist','cors','transform','cache','request_size','response_filter'}
HTTP_METHODS={'get','post','put','patch','delete','options','head'}

def validate_url(value:str, allow_http:bool=False)->str:
    parsed=urlparse(value)
    if parsed.scheme not in ({'http','https'} if allow_http else {'https'}): raise ValueError('URL deve utilizar HTTPS')
    if not parsed.netloc: raise ValueError('Host inválido')
    if parsed.hostname in {'localhost','127.0.0.1','::1'}: raise ValueError('Destino local bloqueado')
    return value

def normalize_openapi(spec:dict)->dict:
    if not isinstance(spec,dict): raise ValueError('Especificação OpenAPI inválida')
    version=str(spec.get('openapi') or spec.get('swagger') or '')
    if not version: raise ValueError('Documento não informa openapi/swagger')
    paths=spec.get('paths') or {}
    if not isinstance(paths,dict): raise ValueError('Paths inválidos')
    operations=[]
    for path,item in paths.items():
        if not str(path).startswith('/') or not isinstance(item,dict): continue
        for method,operation in item.items():
            if method.lower() in HTTP_METHODS and isinstance(operation,dict):
                operations.append({'path':path,'method':method.upper(),'operation_id':operation.get('operationId',''),'summary':operation.get('summary',''),'scopes':list((operation.get('security') or [{}])[0].keys())})
    if len(operations)>5000: raise ValueError('Especificação excede 5000 operações')
    servers=spec.get('servers') or []
    base_url=(servers[0].get('url','') if servers and isinstance(servers[0],dict) else '')
    return {'format':'openapi','version':version,'title':(spec.get('info') or {}).get('title','API importada'),'base_url':base_url,'operations':operations,'raw':spec}

def normalize_wsdl(xml_text:str)->dict:
    try: root=ElementTree.fromstring(xml_text)
    except Exception as exc: raise ValueError('WSDL inválido') from exc
    operations=[]
    for node in root.iter():
        if node.tag.endswith('operation') and node.attrib.get('name'):
            operations.append({'name':node.attrib['name']})
    return {'format':'wsdl','version':'1.1','title':root.attrib.get('name','SOAP Service'),'operations':operations[:5000],'raw_xml':xml_text}

def validate_policy(policy_type:str,config:dict)->dict:
    if policy_type not in POLICY_TYPES: raise ValueError('Política não suportada')
    if policy_type=='rate_limit':
        rpm=int(config.get('requests_per_minute',60)); burst=int(config.get('burst',10))
        if rpm<1 or rpm>100000 or burst<0 or burst>10000: raise ValueError('Limite de requisições inválido')
        config.update(requests_per_minute=rpm,burst=burst)
    elif policy_type=='retry':
        attempts=int(config.get('max_attempts',3)); backoff=int(config.get('backoff_ms',250))
        if attempts<0 or attempts>10 or backoff<0 or backoff>60000: raise ValueError('Política de retry inválida')
        config.update(max_attempts=attempts,backoff_ms=backoff)
    elif policy_type=='timeout':
        timeout=int(config.get('timeout_ms',30000))
        if timeout<100 or timeout>300000: raise ValueError('Timeout inválido')
        config['timeout_ms']=timeout
    return config

def generate_consumer_key()->tuple[str,str]:
    raw='cedp_'+secrets.token_urlsafe(32)
    return raw,hashlib.sha256(raw.encode()).hexdigest()

def hash_secret(raw:str)->str: return hashlib.sha256(raw.encode()).hexdigest()

def safe_code(value:str)->str:
    code=re.sub(r'[^a-z0-9._-]+','-',value.lower()).strip('-')
    if len(code)<2: raise ValueError('Código inválido')
    return code[:100]
