#!/usr/bin/env python3
"""
Script para descargar boletines oficiales de Misiones de forma parametrizada.
Sitio: https://www.boletindigital.misiones.gov.ar/

Uso:
    python descargar_misiones.py                          # Descarga el boletín del día actual
    python descargar_misiones.py --anio 2024              # Descarga todo el año 2024
    python descargar_misiones.py --anio 2024 --mes 5      # Descarga todo mayo de 2024
    python descargar_misiones.py --anio 2024 --mes 5 --dia 15  # Descarga el 15 de mayo de 2024
"""

import os
import re
import requests
import datetime
import calendar
from bs4 import BeautifulSoup
from base import BaseDescargador

URL_BASE = 'https://www.boletindigital.misiones.gov.ar'
URL_PRINCIPAL = f'{URL_BASE}/boletinWEB.html'


def crear_sesion():
    """Crea y configura una sesión HTTP."""
    session = requests.Session()
    session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'es-ES,es;q=0.9,en;q=0.8',
        'Accept-Encoding': 'gzip, deflate, br',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
    })
    return session


def obtener_tokens_sesion(html):
    """Extrae los tokens de sesión ICEfaces necesarios para los POSTs."""
    soup = BeautifulSoup(html, 'html.parser')
    tokens = {}

    for name in ['javax.faces.ViewState', 'javax.faces.ClientWindow', 'ice.window', 'ice.view']:
        inp = soup.find('input', {'name': name})
        if inp:
            tokens[name] = inp.get('value', '')

    # Fallback con regex si BeautifulSoup no los encuentra
    if 'javax.faces.ViewState' not in tokens:
        m = re.search(r'name="javax\.faces\.ViewState"[^>]*value="([^"]+)"', html)
        if not m:
            m = re.search(r'value="([^"]+)"[^>]*name="javax\.faces\.ViewState"', html)
        if m:
            tokens['javax.faces.ViewState'] = m.group(1)

    for campo in ['ice.window', 'ice.view']:
        if campo not in tokens:
            campo_re = campo.replace('.', r'\.')
            m = re.search(rf'name="{campo_re}"[^>]*value="([^"]+)"', html)
            if not m:
                m = re.search(rf'value="([^"]+)"[^>]*name="{campo_re}"', html)
            if m:
                tokens[campo] = m.group(1)

    if 'javax.faces.ClientWindow' not in tokens:
        tokens['javax.faces.ClientWindow'] = tokens.get('ice.window', '')

    if len(tokens) < 3:
        return None

    return tokens


def parsear_boletines(html):
    """Extrae la lista de boletines del HTML de resultados."""
    soup = BeautifulSoup(html, 'html.parser')
    boletines = []

    tarjetas = soup.find_all('div', class_='tarjetaBoletin')
    for tarjeta in tarjetas:
        texto = tarjeta.get_text()
        num_match = re.search(r'Nro[:\s]+(\d+)', texto)
        fecha_span = tarjeta.find('span', class_='text-muted')
        link = tarjeta.find('a', href=re.compile(r'/boletines/\d+\.pdf'))

        if num_match and fecha_span and link:
            numero = num_match.group(1)
            fecha_str = fecha_span.get_text(strip=True)
            url_pdf = URL_BASE + link['href']

            try:
                fecha = datetime.datetime.strptime(fecha_str, "%d/%m/%Y").date()
            except ValueError:
                fecha = None

            boletines.append({
                'numero': numero,
                'fecha': fecha,
                'fecha_str': fecha_str,
                'url_pdf': url_pdf,
            })

    return boletines


def buscar_boletines_mes(anio, mes, session):
    """
    Busca todos los boletines de un mes específico usando el formulario de búsqueda.

    Returns:
        Lista de dicts con {numero, fecha, fecha_str, url_pdf}
    """
    fecha_desde = datetime.date(anio, mes, 1)
    ultimo_dia = calendar.monthrange(anio, mes)[1]
    fecha_hasta = datetime.date(anio, mes, ultimo_dia)

    try:
        # 1. Obtener tokens de sesión desde la página principal
        response_get = session.get(URL_BASE + '/', timeout=30)
        response_get.raise_for_status()

        tokens = obtener_tokens_sesion(response_get.text)
        if not tokens:
            print(f"  ❌ No se pudieron obtener los tokens de sesión para {anio}/{mes:02d}")
            return []

        vs = tokens.get('javax.faces.ViewState', '')
        cw = tokens.get('javax.faces.ClientWindow', tokens.get('ice.window', ''))
        iw = tokens.get('ice.window', '')
        iv = tokens.get('ice.view', '')

        fecha_desde_str = fecha_desde.strftime("%d/%m/%Y")
        fecha_hasta_str = fecha_hasta.strftime("%d/%m/%Y")

        payload = {
            'formEventoWeb': 'formEventoWeb',
            'ice.window': iw,
            'ice.view': iv,
            'javax.faces.ViewState': vs,
            'javax.faces.ClientWindow': cw,
            'ice.submit.type': 'fullSubmit',
            'ice.event.captured': 'formEventoWeb:_t57',
            'formEventoWeb:_t48_input': '',
            'formEventoWeb:fechaDesde_input': fecha_desde_str,
            'formEventoWeb:fechaHasta_input': fecha_hasta_str,
            'formEventoWeb:descripcion_input': '',
            'formEventoWeb:_t57_button': '',
        }

        headers_post = {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Referer': URL_BASE + '/',
            'Origin': URL_BASE,
        }

        # 2. Enviar búsqueda
        response_post = session.post(
            URL_PRINCIPAL, data=payload, headers=headers_post, timeout=30
        )
        response_post.raise_for_status()

        boletines = parsear_boletines(response_post.text)

        # 3. Manejar paginación "Ver Más" si existe (solo en formResultadoWeb, no en el form de búsqueda)
        soup = BeautifulSoup(response_post.text, 'html.parser')
        pagina = 1
        while True:
            ver_mas = soup.find(
                lambda tag: tag.name in ['a', 'button', 'input']
                and re.search(r'Ver M[aá]s', tag.get_text() + tag.get('value', ''), re.IGNORECASE)
                and re.search(r'formResultadoWeb', tag.get('name', '') + tag.get('id', ''))
            )
            if not ver_mas:
                break

            pagina += 1
            tokens2 = obtener_tokens_sesion(response_post.text)
            if not tokens2:
                break

            # Obtener ID del botón Ver Más para ice.event.captured
            btn_id = ver_mas.get('id', 'formResultadoWeb:_t33')
            payload_mas = {
                'formResultadoWeb': 'formResultadoWeb',
                'ice.window': tokens2.get('ice.window', iw),
                'ice.view': tokens2.get('ice.view', iv),
                'javax.faces.ViewState': tokens2.get('javax.faces.ViewState', vs),
                'javax.faces.ClientWindow': tokens2.get('javax.faces.ClientWindow', cw),
                'ice.submit.type': 'fullSubmit',
                'ice.event.captured': btn_id,
                f'{btn_id}_button': '',
            }
            url_resultados = f'{URL_BASE}/resultadoBusquedaAvanzada.html'
            resp_mas = session.post(
                url_resultados, data=payload_mas,
                headers={**headers_post, 'Referer': URL_PRINCIPAL},
                timeout=30
            )
            resp_mas.raise_for_status()

            nuevos = parsear_boletines(resp_mas.text)
            if not nuevos:
                break
            boletines.extend(nuevos)
            soup = BeautifulSoup(resp_mas.text, 'html.parser')
            response_post = resp_mas

        return boletines

    except requests.exceptions.Timeout:
        print(f"  ❌ Timeout al buscar boletines de {anio}/{mes:02d}")
        return []
    except requests.exceptions.RequestException as e:
        print(f"  ❌ Error de conexión: {e}")
        return []
    except Exception as e:
        print(f"  ❌ Error inesperado al buscar: {e}")
        return []


def descargar_pdf(url, numero, fecha, directorio_destino, session):
    """
    Descarga un PDF directamente desde su URL.

    Returns:
        True si se descargó exitosamente, False en caso contrario
    """
    try:
        response = session.get(url, timeout=60)
        response.raise_for_status()

        # Verificar que es un PDF
        if not (response.content[:4] == b'%PDF' or 'pdf' in response.headers.get('Content-Type', '').lower()):
            print(f"  ⚠️  La respuesta no parece un PDF (Content-Type: {response.headers.get('Content-Type', '?')})")
            return False

        archivo_path = os.path.join(
            directorio_destino, f"boletin_{fecha.strftime('%Y-%m-%d')}.pdf"
        )

        with open(archivo_path, 'wb') as f:
            f.write(response.content)

        print(f"  ✅ Descargado: {archivo_path} (Nro {numero})")
        return True

    except requests.exceptions.RequestException as e:
        print(f"  ❌ Error al descargar PDF Nro {numero}: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Error al guardar archivo Nro {numero}: {e}")
        return False


class DescargadorMisiones(BaseDescargador):
    NOMBRE = "MISIONES"
    DIRECTORIO = "files/boletines/misiones/sin_procesar"

    def __init__(self):
        self._session = crear_sesion()
        self._cache_mes: dict = {}  # (anio, mes) -> lista de boletines

    def _boletines_del_mes(self, anio: int, mes: int) -> list:
        key = (anio, mes)
        if key not in self._cache_mes:
            self._cache_mes[key] = buscar_boletines_mes(anio, mes, self._session)
        return self._cache_mes[key]

    def descargar_fecha(self, fecha: datetime.date, directorio: str) -> str:
        boletines = self._boletines_del_mes(fecha.year, fecha.month)
        boletin = next((b for b in boletines if b['fecha'] == fecha), None)

        if boletin is None:
            return 'no_disponible'

        ok = descargar_pdf(boletin['url_pdf'], boletin['numero'], fecha, directorio, self._session)
        return 'ok' if ok else 'error_descarga'


if __name__ == "__main__":
    DescargadorMisiones().run()
