from typing import List, Optional, TypedDict

from app.models.base import Pool
from app.models.mirrors import Origin, Proxy


class BC2Alternative(TypedDict):
    proto: str
    type: str
    created_at: str
    updated_at: str
    url: str


class BC2Site(TypedDict):
    main_domain: str
    available_alternatives: List[BC2Alternative]


class BypassCensorship2(TypedDict):
    version: str
    sites: List[BC2Site]


def onion_alternative(origin: Origin) -> List[BC2Alternative]:
    url: Optional[str] = origin.onion()
    if url is None:
        return []
    return [{
        "proto": "tor",
        "type": "eotk",
        "created_at": str(origin.added),
        "updated_at": str(origin.updated),
        "url": url
    }]


def proxy_alternative(proxy: Proxy) -> Optional[BC2Alternative]:
    if proxy.url is None:
        return None
    return {
        "proto": "https",
        "type": "mirror",
        "created_at": proxy.added.isoformat(),
        "updated_at": proxy.updated.isoformat(),
        "url": proxy.url
    }


def main_domain(origin: Origin) -> str:
    description: str = origin.description
    if description.startswith("proxy:"):
        return description[len("proxy:"):].replace("www.", "")
    domain_name: str = origin.domain_name
    return domain_name.replace("www.", "")


def active_proxies(origin: Origin, pool: Pool) -> List[Proxy]:
    return [
        proxy for proxy in origin.proxies
        if proxy.url is not None and not proxy.deprecated and not proxy.destroyed and proxy.pool_id == pool.id
    ]


def mirror_sites(pool: Pool) -> BypassCensorship2:
    origins = Origin.query.filter(Origin.destroyed.is_(None)).order_by(Origin.domain_name).all()

    sites: List[BC2Site] = []
    for origin in origins:
        # Gather alternatives, filtering out None values from proxy_alternative
        alternatives = onion_alternative(origin) + [
            alt for proxy in active_proxies(origin, pool)
            if (alt := proxy_alternative(proxy)) is not None
        ]

        # Add the site dictionary to the list
        sites.append({
            "main_domain": main_domain(origin),
            "available_alternatives": list(alternatives)
        })

    return {
        "version": "2.0",
        "sites": sites
    }
