import requests
import re
 
# ===============================================================
# FUENTES
# ===============================================================

FUENTES = [
    "http://wb9xxxwh.k34zyer.xyz/get.php?username=Syrge0913T&password=Pwpetpafe121&type=m3u_plus&output=ts",
    "https://drive.google.com/uc?export=download&id=1FutBqDBBVka99fevlRpbQgidH1hlx9CN",
]

ARCHIVO_SALIDA = "/mnt/nvme/iptv/lista_filtrada.m3u"


# ===============================================================
# PATRONES DE BÚSQUEDA
# ===============================================================

PATRON_FUTBOL = re.compile(
    r'\b(FOOTBALL|FUTBOL|SOCCER|LALIGA|LA\s+LIGA|PREMIER|CHAMPIONS|UEFA|FIFA|'
    r'DAZN|BEIN|ESPN|EUROSPORT|SKY\s+SPORT|BT\s+SPORT|TNT\s+SPORT|ELEVEN\s+SPORT|'
    r'MOVISTAR\s+LIGA|MOVISTAR\s+SPORT|GOL\s+TV|GOL\s+PLAY|SETANTA|GO3\s+SPORT|'
    r'ARENA\s+SPORT|SPORT\s+KLUB|SWISS\s+SPORT|RTS\s+SPORT|SRF\s+SPORT)\b',
    re.IGNORECASE
)

PATRON_F1 = re.compile(
    r'\b(FORMULA\s*1|FORMULA\s+ONE|F1|MOTORSPORT|MOTORSPORTS|F1\s+TV|F1TV|'
    r'SKY\s+SPORTS\s+F1|SKY\s+F1|F1\s+LIVE)\b',
    re.IGNORECASE
)

PATRON_ADULT = re.compile(
    r'\b(ADULT|XXX|PENTHOUSE|PLAYBOY|EROTIC|EROTICA|BLUE\s+HUSTLER|HUSTLER|'
    r'DORCEL|BABELAND|VIVID|WICKED|PRIVATE|SEX|SEXTV|PORNO|PORN|HARDCORE)\b',
    re.IGNORECASE
)


# ===============================================================
# PATRONES DE PAÍSES
# ===============================================================

GRUPOS_PAIS = {

    "Espana": [
        r'\bSPAIN\b',
        r'\bESPANA\b',
        r'\bESPAÑA\b',
        r'(^|[, ])ES:',
        r'\|ES\b',
        r'\bES\s*:'
    ],

    "Polonia": [
        r'\bPOLAND\b',
        r'\bPOLSKA\b',
        r'\bPOLISH\b',
        r'(^|[, ])PL:',
        r'\|PL\b',
        r'\bPL\s*:'
    ],

    "United Kingdom": [
        r'\bUNITED KINGDOM\b',
        r'\bUK\b',
        r'\bENGLAND\b',
        r'\bBRITAIN\b',
        r'(^|[, ])UK:',
        r'(^|[, ])GB:',
        r'\|UK\b',
        r'\|GB\b'
    ],

    "Latino": [
        r'\bLATINO\b',
        r'\bLATAM\b',
        r'\bLATIN\b',
        r'\bMEXICO\b',
        r'\bARGENTINA\b',
        r'\bCOLOMBIA\b',
        r'\bCHILE\b',
        r'\bPERU\b',
        r'\bVENEZUELA\b'
    ],
}


# ===============================================================
# ORDEN PRIORITARIO ESPAÑA
#
# Las cadenas principales aparecen primero.
# Todo lo que no esté aquí aparecerá después alfabéticamente.
# ===============================================================

ORDEN_ESPANA = [

    # ===========================================================
    # TDT PRINCIPAL
    # ===========================================================

    "LA 1",
    "LA 1 CANARIAS",
    "TVE 1",
    "LA1",
    "TVE1",

    "LA 2",
    "TVE 2",
    "LA2",
    "TVE2",

    "ANTENA 3",
    "ANTENA3",

    "CUATRO",

    "TELECINCO",
    "TELE 5",
    "TELE5",

    "LA SEXTA",
    "LASEXTA",
    "LA6",

    # ===========================================================
    # RTVE
    # ===========================================================

    "CANAL 24 HORAS",
    "24 HORAS",

    "TELEDEPORTE",

    "CLAN",

    # ===========================================================
    # ATRESMEDIA
    # ===========================================================

    "NEOX",
    "NOVA",
    "MEGA",
    "ATRESERIES",

    # ===========================================================
    # MEDIASET
    # ===========================================================

    "FDF",
    "FACTORIA DE FICCION",
    "DIVINITY",
    "ENERGY",
    "BE MAD",
    "BEMAD",

    # ===========================================================
    # OTROS TDT
    # ===========================================================

    "TRECE",
    "DMAX",
    "PARAMOUNT",

    "DISNEY CHANNEL",

    # ===========================================================
    # DOCUMENTALES
    # ===========================================================

    "DISCOVERY",
    "NATIONAL GEOGRAPHIC",
    "NAT GEO",
    "ODISEA",
    "HISTORY",
    "HISTORY CHANNEL",

    # ===========================================================
    # CINE / SERIES
    # ===========================================================

    "AXN",
    "AXN WHITE",
    "CALLE 13",
    "SYFY",
    "WARNER",
    "COMEDY CENTRAL",
    "AMC",
    "FOX",
    "TCM",

    # ===========================================================
    # DEPORTES
    # ===========================================================

    "EUROSPORT",
    "GOL PLAY",
    "GOL TV",

    # ===========================================================
    # MOVISTAR
    # ===========================================================

    "MOVISTAR",
]
# ===============================================================
# ORDEN POLONIA
# ===============================================================

ORDEN_POLONIA = [
    "TVP 1",
    "TVP 2",
    "TVP 3",
    "TVP",
    "TVN 24",
    "TVN7",
    "TVN",
    "POLSAT NEWS",
    "POLSAT SPORT",
    "POLSAT",
    "TTV",
    "TV4",
    "TV6"
]


# ===============================================================
# ORDEN UK
# ===============================================================

ORDEN_UK = [
    "BBC ONE",
    "BBC TWO",
    "BBC THREE",
    "BBC FOUR",
    "BBC NEWS",
    "BBC",
    "ITV 1",
    "ITV 2",
    "ITV 3",
    "ITV 4",
    "ITV",
    "CHANNEL 4",
    "CHANNEL 5",
    "SKY NEWS",
    "SKY ONE",
    "SKY ATLANTIC",
    "SKY MAX",
    "SKY"
]


# ===============================================================
# REGEX GROUP-TITLE
# ===============================================================

RE_GROUP = re.compile(
    r'(group-title=")[^"]*(")',
    re.IGNORECASE
)


# ===============================================================
# NORMALIZAR GRUPO
# ===============================================================

def normalizar_grupo(extinf_line, nombre_grupo):

    if RE_GROUP.search(extinf_line):

        return RE_GROUP.sub(
            r'\g<1>' + nombre_grupo + r'\g<2>',
            extinf_line,
            count=1
        )

    return extinf_line.replace(
        '#EXTINF:-1',
        f'#EXTINF:-1 group-title="{nombre_grupo}"',
        1
    )


# ===============================================================
# COMPROBAR SI ES CANAL EN VIVO
# ===============================================================

def es_canal_en_vivo(url):

    url_lower = url.lower()

    if "/movie/" in url_lower:
        return False

    if "/series/" in url_lower:
        return False

    if url_lower.endswith(
        (
            '.mp4',
            '.mkv',
            '.avi',
            '.mp3',
            '.mov',
            '.flv'
        )
    ):
        return False

    return True


# ===============================================================
# OBTENER CATEGORÍA
# ===============================================================

def obtener_categoria(extinf_line):

    texto = extinf_line.upper()

    # -----------------------------------------------------------
    # MOTOR / ADULTOS
    # -----------------------------------------------------------

    if PATRON_ADULT.search(texto):
        return "Motor"

    # -----------------------------------------------------------
    # F1
    # -----------------------------------------------------------

    if PATRON_F1.search(texto):
        return "F1"

    # -----------------------------------------------------------
    # FÚTBOL
    # -----------------------------------------------------------

    if PATRON_FUTBOL.search(texto):
        return "Futbol"

    # -----------------------------------------------------------
    # PAÍSES
    # -----------------------------------------------------------

    for nombre_grupo, patrones in GRUPOS_PAIS.items():

        for patron in patrones:

            if re.search(
                patron,
                texto,
                re.IGNORECASE
            ):
                return nombre_grupo

    return None


# ===============================================================
# PESO DE ORDEN
# ===============================================================

def obtener_peso_orden(extinf_line, lista_prioridades):

    # IMPORTANTE:
    # Usamos SOLO el nombre real del canal, después de la última coma.
    # Ignoramos tvg-id y tvg-name porque la fuente puede tenerlos mal.
    if "," in extinf_line:
        nombre_canal = extinf_line.rsplit(",", 1)[1].strip()
    else:
        nombre_canal = extinf_line.strip()

    nombre_upper = nombre_canal.upper()

    # Quitar prefijo ES:
    nombre_upper = re.sub(
        r'^ES\s*:\s*',
        '',
        nombre_upper
    ).strip()

    for indice, patron in enumerate(lista_prioridades):

        patron_upper = patron.upper()

        # Coincidencia exacta
        if nombre_upper == patron_upper:
            return indice

        # Coincidencia permitiendo calidad al final
        # Ejemplo:
        # LA 1
        # LA 1 HD
        # LA 1 FHD
        # LA 1 UHD
        # LA 1 4K
        if re.match(
            r'^' + re.escape(patron_upper) +
            r'(?:\s+(?:FHD|UHD|HD|SD|4K|HEVC))?$',
            nombre_upper
        ):
            return indice

    # Todo lo que no esté en la lista va después
    return 999

def clave_duplicado(extinf_line):
    """
    Genera una clave normalizada para detectar duplicados.

    Mantiene separadas las distintas calidades:
    LA 1 FHD
    LA 1 HD
    LA 1 SD

    Pero elimina duplicados exactos como:
    LA 1 HD
    LA 1 HD
    """

    if "," in extinf_line:
        nombre = extinf_line.rsplit(",", 1)[1].strip()
    else:
        nombre = extinf_line.strip()

    # Quitar prefijos de país
    nombre = re.sub(
        r'^(ES|PL|UK|GB)\s*:\s*',
        '',
        nombre,
        flags=re.IGNORECASE
    ).strip()

    # Normalizar espacios
    nombre = re.sub(
        r'\s+',
        ' ',
        nombre
    )

    # Normalizar calidad
    nombre = re.sub(
        r'\s+(FHD|UHD|HD|SD|4K|HEVC)\s*$',
        lambda m: " " + m.group(1).upper(),
        nombre,
        flags=re.IGNORECASE
    )

    return nombre.upper()

def eliminar_duplicados(canales_por_categoria):

    eliminados_total = 0

    for categoria, lista_canales in canales_por_categoria.items():

        vistos = set()
        canales_limpios = []

        for extinf, url, fuente in lista_canales:

            clave = clave_duplicado(extinf)

            if clave in vistos:
                eliminados_total += 1
                continue

            vistos.add(clave)

            canales_limpios.append(
                (
                    extinf,
                    url,
                    fuente
                )
            )

        canales_por_categoria[categoria] = canales_limpios

    return eliminados_total

# ===============================================================
# PROCESAR UNA FUENTE
# ===============================================================

def procesar_respuesta(
    r,
    canales_por_categoria,
    contador_por_fuente,
    indice_fuente
):

    pending_extinf = None

    total_extinf = 0
    total_urls = 0
    total_guardados = 0
    total_descartados = 0

    for line in r.iter_lines():

        if not line:
            continue

        # -------------------------------------------------------
        # DECODIFICAR
        # -------------------------------------------------------

        try:

            line_str = line.decode(
                "utf-8"
            ).strip()

        except UnicodeDecodeError:

            line_str = line.decode(
                "latin-1",
                errors="replace"
            ).strip()

        # -------------------------------------------------------
        # EXTINF
        # -------------------------------------------------------

        if line_str.startswith("#EXTINF"):

            pending_extinf = line_str

            total_extinf += 1

            continue

        # -------------------------------------------------------
        # EXT-VLC-OPT
        # -------------------------------------------------------

        if line_str.startswith("#EXTVLCOPT"):

            continue

        # -------------------------------------------------------
        # URL
        # -------------------------------------------------------

        if (
            not line_str.startswith("#")
            and pending_extinf
        ):

            total_urls += 1

            if es_canal_en_vivo(line_str):

                categoria = obtener_categoria(
                    pending_extinf
                )

                if categoria in canales_por_categoria:

                    extinf_modificado = normalizar_grupo(
                        pending_extinf,
                        categoria
                    )

                    canales_por_categoria[
                        categoria
                    ].append(
                        (
                            extinf_modificado,
                            line_str,
                            indice_fuente
                        )
                    )

                    total_guardados += 1

                else:

                    total_descartados += 1

            pending_extinf = None

    # -----------------------------------------------------------
    # ESTADÍSTICAS
    # -----------------------------------------------------------

    contador_por_fuente[indice_fuente] = {

        "extinf": total_extinf,
        "urls": total_urls,
        "guardados": total_guardados,
        "descartados": total_descartados,

    }


# ===============================================================
# FUNCIÓN PRINCIPAL
# ===============================================================

def filtrar():

    headers = {

        "User-Agent":
            "Mozilla/5.0 "
            "(Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 "
            "(KHTML, like Gecko) "
            "Chrome/131.0 Safari/537.36",

        "Accept": "*/*",

    }


    # ===========================================================
    # CATEGORÍAS
    # ===========================================================

    canales_por_categoria = {

        "Espana": [],
        "Polonia": [],
        "United Kingdom": [],
        "Latino": [],
        "Futbol": [],
        "F1": [],
        "Motor": [],

    }


    contador_por_fuente = {}


    # ===========================================================
    # INICIO
    # ===========================================================

    print()
    print("=" * 65)
    print(" IPTV - PROCESANDO MÚLTIPLES FUENTES ")
    print("=" * 65)


    # ===========================================================
    # PROCESAR FUENTES
    # ===========================================================

    for i, url_fuente in enumerate(
        FUENTES,
        start=1
    ):

        print()
        print("-" * 65)
        print(f"FUENTE {i}")
        print("-" * 65)

        try:

            print(
                f"Descargando fuente {i}..."
            )

            with requests.get(
                url_fuente,
                stream=True,
                timeout=60,
                headers=headers
            ) as r:

                r.raise_for_status()

                print(
                    f"HTTP: {r.status_code}"
                )

                print(
                    "Content-Type:",
                    r.headers.get(
                        "content-type",
                        "desconocido"
                    )
                )

                procesar_respuesta(
                    r,
                    canales_por_categoria,
                    contador_por_fuente,
                    i
                )

            datos = contador_por_fuente[i]

            print()
            print(
                f"EXTINF leídos:     {datos['extinf']}"
            )

            print(
                f"URLs encontradas:  {datos['urls']}"
            )

            print(
                f"Canales guardados:  {datos['guardados']}"
            )

            print(
                f"Descartados:        {datos['descartados']}"
            )

        except Exception as e:

            print()
            print(
                f"ERROR procesando fuente {i}:"
            )

            print(e)

            contador_por_fuente[i] = {

                "extinf": 0,
                "urls": 0,
                "guardados": 0,
                "descartados": 0

            }

    # ===========================================================
    # ELIMINAR DUPLICADOS
    # ===========================================================

    print()
    print("Eliminando duplicados...")

    duplicados_eliminados = eliminar_duplicados(
        canales_por_categoria
    )

    print(
        f"Duplicados eliminados: {duplicados_eliminados}"
    )




    # ===========================================================
    # ORDENAR ESPAÑA
    # ===========================================================

    canales_por_categoria[
        "Espana"
    ].sort(
        key=lambda canal: (
            obtener_peso_orden(
                canal[0],
                ORDEN_ESPANA
            ),
            canal[0].upper()
        )
    )


    # ===========================================================
    # ORDENAR POLONIA
    # ===========================================================

    canales_por_categoria[
        "Polonia"
    ].sort(
        key=lambda canal: (
            obtener_peso_orden(
                canal[0],
                ORDEN_POLONIA
            ),
            canal[0].upper()
        )
    )


    # ===========================================================
    # ORDENAR UK
    # ===========================================================

    canales_por_categoria[
        "United Kingdom"
    ].sort(
        key=lambda canal: (
            obtener_peso_orden(
                canal[0],
                ORDEN_UK
            ),
            canal[0].upper()
        )
    )


    # ===========================================================
    # ESCRIBIR M3U
    # ===========================================================

    total = 0

    print()
    print(
        "Escribiendo lista final..."
    )

    with open(
        ARCHIVO_SALIDA,
        "w",
        encoding="utf-8"
    ) as f:

        f.write(
            "#EXTM3U\n"
        )

        for categoria, lista_canales in (
            canales_por_categoria.items()
        ):

            for extinf, url, fuente in lista_canales:

                f.write(
                    extinf + "\n"
                )

                f.write(
                    url + "\n"
                )

                total += 1


    # ===========================================================
    # RESULTADO
    # ===========================================================

    print()
    print("=" * 65)

    print(
        "LISTA GENERADA CORRECTAMENTE"
    )

    print(
        f"Archivo: {ARCHIVO_SALIDA}"
    )

    print(
        f"Total de canales: {total}"
    )

    print("=" * 65)


    # ===========================================================
    # RESUMEN POR FUENTE
    # ===========================================================

    print()
    print("RESUMEN POR FUENTE")
    print("-" * 65)

    for i, url_fuente in enumerate(
        FUENTES,
        start=1
    ):

        datos = contador_por_fuente.get(
            i,
            {
                "extinf": 0,
                "urls": 0,
                "guardados": 0,
                "descartados": 0
            }
        )

        print()
        print(
            f"Fuente {i}:"
        )

        print(
            f"  EXTINF:       {datos['extinf']}"
        )

        print(
            f"  URLs:         {datos['urls']}"
        )

        print(
            f"  Guardados:    {datos['guardados']}"
        )

        print(
            f"  Descartados:  {datos['descartados']}"
        )


    # ===========================================================
    # RESUMEN POR CATEGORÍA
    # ===========================================================

    print()
    print("RESUMEN POR CATEGORÍA")
    print("-" * 65)

    for categoria, lista in (
        canales_por_categoria.items()
    ):

        print(
            f"{categoria:20s}: "
            f"{len(lista):6d} canales"
        )


    # ===========================================================
    # DESGLOSE POR FUENTE
    # ===========================================================

    print()
    print("DESGLOSE POR CATEGORÍA Y FUENTE")
    print("-" * 65)

    for categoria, lista in (
        canales_por_categoria.items()
    ):

        fuente_1 = sum(
            1
            for _, _, fuente in lista
            if fuente == 1
        )

        fuente_2 = sum(
            1
            for _, _, fuente in lista
            if fuente == 2
        )

        print(
            f"{categoria:20s}: "
            f"Fuente 1 = {fuente_1:5d} | "
            f"Fuente 2 = {fuente_2:5d}"
        )


    print()
    print("=" * 65)


# ===============================================================
# EJECUTAR
# ===============================================================

if __name__ == "__main__":

    filtrar()
