import hashlib, time
from datetime import datetime, timezone
from sqlalchemy import text, select
from app.core.database import SessionLocal
from app.models.entities import ServiceHealthSnapshot, OperationalIncident, ObservabilityAlertPolicy
from app.core.config import settings

COUNTERS={'http_requests_total':0,'http_errors_total':0}
HIST={'http_duration_ms_sum':0,'http_duration_ms_count':0}

def record_http(status:int,duration_ms:int):
    COUNTERS['http_requests_total']+=1
    if status>=500: COUNTERS['http_errors_total']+=1
    HIST['http_duration_ms_sum']+=duration_ms; HIST['http_duration_ms_count']+=1

def prometheus_text():
    avg=HIST['http_duration_ms_sum']/max(1,HIST['http_duration_ms_count'])
    return '\n'.join([
      '# HELP cedp_http_requests_total Total HTTP requests','# TYPE cedp_http_requests_total counter',f"cedp_http_requests_total {COUNTERS['http_requests_total']}",
      '# HELP cedp_http_errors_total Total HTTP 5xx errors','# TYPE cedp_http_errors_total counter',f"cedp_http_errors_total {COUNTERS['http_errors_total']}",
      '# HELP cedp_http_duration_ms_average Average HTTP duration','# TYPE cedp_http_duration_ms_average gauge',f'cedp_http_duration_ms_average {avg:.3f}',
      ''])

def _check(name,fn):
    started=time.perf_counter()
    try: details=fn() or {}; status='healthy'
    except Exception as exc: status='unhealthy'; details={'error':str(exc)[:500]}
    return {'service_name':name,'status':status,'latency_ms':int((time.perf_counter()-started)*1000),'details':details}

def collect_health():
    import redis, boto3
    def db_check():
        db=SessionLocal()
        try: return {'result':db.execute(text('SELECT 1')).scalar_one()}
        finally: db.close()
    def redis_check():
        r=redis.Redis.from_url(settings.redis_url); return {'ping':bool(r.ping())}
    def storage_check():
        s=boto3.client('s3',endpoint_url=settings.s3_endpoint,aws_access_key_id=settings.s3_access_key,aws_secret_access_key=settings.s3_secret_key,verify=settings.s3_secure)
        s.head_bucket(Bucket=settings.s3_bucket); return {'bucket':settings.s3_bucket}
    results=[_check('metadata-db',db_check),_check('redis',redis_check),_check('object-storage',storage_check)]
    db=SessionLocal()
    try:
        for item in results: db.add(ServiceHealthSnapshot(**item))
        db.commit()
    finally: db.close()
    return results

def evaluate_policies():
    metrics={**COUNTERS,'http_duration_ms_average':HIST['http_duration_ms_sum']/max(1,HIST['http_duration_ms_count'])}
    db=SessionLocal(); opened=[]
    try:
        policies=db.scalars(select(ObservabilityAlertPolicy).where(ObservabilityAlertPolicy.enabled.is_(True))).all()
        ops={'gt':lambda a,b:a>b,'gte':lambda a,b:a>=b,'lt':lambda a,b:a<b,'lte':lambda a,b:a<=b,'eq':lambda a,b:a==b}
        for p in policies:
            value=metrics.get(p.metric_name)
            if value is None or not ops.get(p.operator,ops['gt'])(value,p.threshold): continue
            fp=hashlib.sha256(f'{p.id}:{p.metric_name}'.encode()).hexdigest()
            existing=db.scalar(select(OperationalIncident).where(OperationalIncident.fingerprint==fp,OperationalIncident.status.in_(['open','acknowledged'])))
            if not existing:
                incident=OperationalIncident(title=f'Alerta: {p.name}',description=f'{p.metric_name} {p.operator} {p.threshold}',source='observability-policy',severity=p.severity,fingerprint=fp,evidence={'metric':p.metric_name,'value':value,'threshold':p.threshold})
                db.add(incident); opened.append(p.id); p.last_triggered_at=datetime.now(timezone.utc)
        db.commit(); return {'opened':opened}
    finally: db.close()
