import json
import urllib.request
import urllib.error
from datetime import datetime

from django.db import connection
from django.utils import timezone
from common.utils import clean_sql_string, clean_sql_int


# =========================================================
# Helper HTTP (stdlib) — evita dependencia extra en requirements
# =========================================================
def _http_get_json(url, timeout=8):
    """GET a un servicio externo que responde JSON. Usa stdlib para no añadir deps."""
    req = urllib.request.Request(url, headers={'User-Agent': 'django-core-base/1.0'})
    with urllib.request.urlopen(req, timeout=timeout) as response:
        raw = response.read().decode('utf-8')
        try:
            return json.loads(raw)
        except json.JSONDecodeError:
            return {'raw': raw}


def _http_get_text(url, timeout=8):
    """GET a un servicio externo que responde texto plano."""
    req = urllib.request.Request(url, headers={'User-Agent': 'django-core-base/1.0'})
    with urllib.request.urlopen(req, timeout=timeout) as response:
        return response.read().decode('utf-8').strip()


# =========================================================
# ACCIÓN 1 — Base de datos (INSERT histórico de ejecución)
# set_parameterized
# =========================================================
def setEjecucion(params):
    """
    Inserta una fila en automatizacion_ejecuciones registrando una ejecución.
    params esperados: nombre, descripcion, estado, origen, resultado, error
    """
    query = """
        INSERT INTO automatizacion_ejecuciones
            (nombre, descripcion, estado, origen, resultado, error, fecha_inicio, fecha_fin, activo)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
        RETURNING id
    """

    ahora = timezone.now()
    resultado_json = json.dumps(params.get('resultado')) if params.get('resultado') is not None else None

    parameter_dict = [
        clean_sql_string(params.get('nombre')),
        clean_sql_string(params.get('descripcion')),
        clean_sql_string(params.get('estado') or 'ok'),
        clean_sql_string(params.get('origen') or 'manual'),
        resultado_json,
        clean_sql_string(params.get('error')) if params.get('error') else None,
        ahora,
        ahora,
        bool(params.get('activo', True)),
    ]

    with connection.cursor() as cursor:
        cursor.execute(query, parameter_dict)
        row = cursor.fetchone()
        nuevo_id = row[0] if row else None

    return {'id': nuevo_id, 'fecha': ahora.isoformat()}


# =========================================================
# ACCIÓN 2 — Servicio externo: Cat Fact API
# https://catfact.ninja/fact (gratis, sin auth)
# =========================================================
def getCatFact():
    url = 'https://catfact.ninja/fact'
    data = _http_get_json(url)
    return {
        'servicio': 'catfact.ninja',
        'url': url,
        'fact': data.get('fact'),
        'length': data.get('length'),
    }


# =========================================================
# ACCIÓN 3 — Servicio externo: GitHub Zen
# https://api.github.com/zen (gratis, sin auth)
# =========================================================
def getGithubZen():
    url = 'https://api.github.com/zen'
    texto = _http_get_text(url)
    return {
        'servicio': 'api.github.com/zen',
        'url': url,
        'zen': texto,
    }


# =========================================================
# Orquestador — ejecuta las 3 acciones en secuencia
# =========================================================
def ejecutarAutomatizaciones(params):
    """
    Ejecuta las 3 acciones automatizadas:
      1) Llamada a servicio externo: catfact.ninja
      2) Llamada a servicio externo: api.github.com/zen
      3) Persistencia en BD: inserta la ejecución en automatizacion_ejecuciones
    """
    resultados = {'acciones': []}
    errores = []

    # Acción 2 — catfact
    try:
        resultados['acciones'].append({'step': 1, 'ok': True, 'data': getCatFact()})
    except Exception as err:
        errores.append(f'catfact: {err}')
        resultados['acciones'].append({'step': 1, 'ok': False, 'error': str(err)})

    # Acción 3 — github zen
    try:
        resultados['acciones'].append({'step': 2, 'ok': True, 'data': getGithubZen()})
    except Exception as err:
        errores.append(f'github_zen: {err}')
        resultados['acciones'].append({'step': 2, 'ok': False, 'error': str(err)})

    # Acción 1 — persistir en BD
    estado_final = 'ok' if not errores else 'error'
    nombre = clean_sql_string(params.get('nombre')) or f'Ejecucion {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
    origen = clean_sql_string(params.get('origen')) or 'manual'

    registro = setEjecucion({
        'nombre': nombre,
        'descripcion': params.get('descripcion') or 'Ejecución orquestada por /api/automatizados/ejecutar/',
        'estado': estado_final,
        'origen': origen,
        'resultado': resultados,
        'error': '; '.join(errores) if errores else None,
        'activo': True,
    })

    resultados['acciones'].append({'step': 3, 'ok': True, 'data': registro})

    return {
        'ejecucion_id': registro.get('id'),
        'estado': estado_final,
        'total_acciones': len(resultados['acciones']),
        'resultados': resultados,
        'errores': errores,
    }


# =========================================================
# Lectura — historial (get_parameterized)
# =========================================================
def getHistorial(params):
    """
    Devuelve el histórico de ejecuciones. Permite filtrar por estado y limit.
    """
    query = """
        SELECT id, nombre, descripcion, estado, origen, resultado, error,
               fecha_inicio, fecha_fin, activo
        FROM automatizacion_ejecuciones
        WHERE (%s = '' OR estado = %s)
        ORDER BY fecha_inicio DESC
        LIMIT %s
    """

    estado = clean_sql_string(params.get('estado')) if params.get('estado') else ''
    limite = clean_sql_int(params.get('limit')) or 20

    parameter_dict = [estado, estado, limite]

    with connection.cursor() as cursor:
        cursor.execute(query, parameter_dict)
        columns = [col[0] for col in cursor.description]
        rows = [dict(zip(columns, row)) for row in cursor.fetchall()]

    # Normalizamos el campo resultado (JSON serializado en SQLite)
    for row in rows:
        if isinstance(row.get('resultado'), str):
            try:
                row['resultado'] = json.loads(row['resultado'])
            except (ValueError, TypeError):
                pass

    return {'total': len(rows), 'data': rows}


# =========================================================
# Estado del módulo
# =========================================================
def getEstado():
    """
    Devuelve el estado general del módulo de automatizaciones:
    cuántas ejecuciones hay, última ejecución, endpoints disponibles.
    """
    query = """
        SELECT COUNT(*) AS total,
               SUM(CASE WHEN estado = 'ok' THEN 1 ELSE 0 END) AS ok,
               SUM(CASE WHEN estado = 'error' THEN 1 ELSE 0 END) AS errores,
               MAX(fecha_inicio) AS ultima_ejecucion
        FROM automatizacion_ejecuciones
    """
    with connection.cursor() as cursor:
        cursor.execute(query)
        columns = [col[0] for col in cursor.description]
        row = cursor.fetchone()
        resumen = dict(zip(columns, row)) if row else {}

    return {
        'status': 'ok',
        'modulo': 'automatizados',
        'endpoints': [
            'POST /api/automatizados/ejecutar/',
            'POST /api/automatizados/historial/',
            'POST /api/automatizados/estado/',
        ],
        'resumen': resumen,
    }
