# utils/downloader.py

import os
import asyncio
import aiohttp
import logging
from bs4 import BeautifulSoup
from calendar import monthrange
from urllib.parse import urljoin, urlparse
from config import (
    BATCH_SIZE,
    BASE_URL,
    FILE_PATH,
    OUTPUT_DIR,
    SETTINGS
)
from utils.helpers import get_media_types

# ---------------- Настройка логирования ----------------
LOG_FILE = os.path.join(OUTPUT_DIR, "downloader.log")
os.makedirs(OUTPUT_DIR, exist_ok=True)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler(LOG_FILE, encoding="utf-8"),
        logging.StreamHandler()
    ]
)

# создаём выходные каталоги
VIDEO_DIR = os.path.join(OUTPUT_DIR, "video")
IMG_DIR = os.path.join(OUTPUT_DIR, "images")
os.makedirs(VIDEO_DIR, exist_ok=True)
os.makedirs(IMG_DIR, exist_ok=True)

# очищаем файл
# with open(FILE_PATH, "w", encoding="utf-8") as f:
#     pass


# ---------------- Проверка мультимедиа ----------------
async def check_media(session: aiohttp.ClientSession, url: str, media=None) -> bool:
    if media is None:
        media = get_media_types()
    try:
        async with session.get(url, timeout=10) as resp:
            if resp.status != 200:
                return False
            text = await resp.text()
    except Exception as e:
        logging.warning(f"Ошибка при проверке {url}: {e}")
        return False

    soup = BeautifulSoup(text, "html.parser")
    return any(soup.find(tag) for tag in media)


async def check_and_collect(session, url, file_handle, collection, media=None):
    if await check_media(session, url, media):
        logging.info(f"[+] Медиа найдено: {url}")
        file_handle.write(url + "\n")
        collection.append(url)
    else:
        logging.info(f"[-] Нет медиа: {url}")


# ---------------- Генерация URL ----------------
def generate_urls(symbols_list: list, max_id: int = None):
    """Генерируем URL в формате key-month-day[-id]."""
    if max_id is None:
        max_id = SETTINGS.get("max_id", 10)

    urls = []
    for sym in symbols_list:
        base_sym = sym.strip()
        for month in range(1, 13):
            days_in_month = monthrange(2025, month)[1]
            for day in range(1, days_in_month + 1):
                # базовый URL без id
                urls.append(
                    f"https://{BASE_URL}/{base_sym}-{month:02d}-{day:02d}")
                # URL с id
                for i in range(1, max_id + 1):
                    urls.append(
                        f"https://{BASE_URL}/{base_sym}-{month:02d}-{day:02d}-{i}")
    logging.info(f"[+] Сгенерировано {len(urls)} URL")
    return urls


# ---------------- Получение ссылок медиа ----------------
async def fetch_media_urls(session: aiohttp.ClientSession, page_url: str, media=None):
    if media is None:
        media = get_media_types()

    media_urls = []
    try:
        async with session.get(page_url, timeout=20) as resp:
            if resp.status != 200:
                logging.warning(
                    f"[-] Не удалось открыть страницу {page_url} (status {resp.status})")
                return media_urls
            text = await resp.text()
    except Exception as e:
        logging.warning(f"[-] Ошибка при открытии страницы {page_url}: {e}")
        return media_urls

    soup = BeautifulSoup(text, "html.parser")
    for tag_type in media:
        for tag in soup.find_all(tag_type):
            src = tag.get("src")
            if src:
                src = src.strip()
                # Если абсолютный URL — используем напрямую
                if src.startswith("http"):
                    full_url = src
                # Если src начинается с BASE_URL — добавляем протокол
                elif src.startswith(BASE_URL):
                    full_url = f"https://{src}"
                # Если src относительный — соединяем с page_url
                else:
                    full_url = urljoin(page_url, src)
                media_urls.append((full_url, tag_type))

    logging.info(f"[+] Найдено {len(media_urls)} медиа на странице {page_url}")
    return media_urls


# ---------------- Скачивание медиа ----------------
async def download_file(session: aiohttp.ClientSession, media_url: str, tag_type: str = "video"):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                      "(KHTML, как Gecko) Chrome/142.0.0.0 Safari/537.36"
    }
    target_dir = VIDEO_DIR if tag_type == "video" else IMG_DIR

    try:
        async with session.get(media_url, headers=headers, timeout=20) as resp:
            if resp.status != 200:
                logging.error(
                    f"[-] Не удалось скачать {media_url} (status {resp.status})")
                return

            path = urlparse(media_url).path
            filename = os.path.basename(path) or "file_from_url"
            file_path = os.path.join(target_dir, filename)

            content = await resp.read()
            with open(file_path, "wb") as f:
                f.write(content)

            logging.info(f"[+] Скачано: {file_path}")

    except Exception as e:
        logging.error(f"[-] Ошибка при скачивании {media_url}: {e}")


async def process_page(session: aiohttp.ClientSession, page_url: str, media=None):
    urls = await fetch_media_urls(session, page_url, media)
    tasks = [download_file(session, url, tag_type) for url, tag_type in urls]
    await asyncio.gather(*tasks)


async def download_from_pages(page_urls: list, media=None):
    if media is None:
        media = get_media_types()

    async with aiohttp.ClientSession() as session:
        tasks = [process_page(session, url, media) for url in page_urls]
        await asyncio.gather(*tasks)


# ---------------- Основной батч-процесс ----------------
async def process_symbols(symbols_list: list):
    batch_count = 0
    found_pages_total = 0

    async with aiohttp.ClientSession() as session:
        with open(FILE_PATH, "a", encoding="utf-8") as f:
            for sym in symbols_list:
                all_urls = generate_urls([sym])
                for i in range(0, len(all_urls), BATCH_SIZE):
                    batch = all_urls[i:i + BATCH_SIZE]
                    batch_count += 1
                    print(
                        f"[+] Батч {batch_count}: проверяем {len(batch)} URL...")

                    found_pages_batch = []
                    tasks = [check_and_collect(
                        session, url, f, found_pages_batch) for url in batch]
                    await asyncio.gather(*tasks)

                    found_pages_total += len(found_pages_batch)
                    if found_pages_batch:
                        print(
                            f"[+] Начинаем скачивание медиа с {len(found_pages_batch)} страниц...")
                        await download_from_pages(found_pages_batch)

                    await asyncio.sleep(1)

    print(
        f"[+] Обработка завершена. Всего найдено медиа на {found_pages_total} страницах.")
