"""Validação estrita de todos os dados enviados pelo painel (campos desconhecidos são rejeitados)."""
import base64
import ipaddress
import re
from typing import Annotated, Literal, Optional

from pydantic import AfterValidator, BaseModel, ConfigDict, Field


def _url(v: str) -> str:
    v = v.strip()
    if v and not (v.startswith("https://") or v.startswith("http://") or v.startswith("/api/media/")):
        raise ValueError("URL deve começar com https://, http:// ou /api/media/")
    if len(v) > 1000:
        raise ValueError("URL muito longa")
    return v


def _req_url(v: str) -> str:
    v = _url(v)
    if not v:
        raise ValueError("URL obrigatória")
    return v


def _ip(v: str) -> str:
    ipaddress.ip_address(v.strip())
    return v.strip()


def _wg_key(v: str) -> str:
    v = v.strip()
    if v:
        try:
            if len(base64.b64decode(v, validate=True)) != 32:
                raise ValueError()
        except Exception:
            raise ValueError("Chave WireGuard inválida (base64 de 32 bytes)")
    return v


def _cidr(v: str) -> str:
    net = ipaddress.ip_network(v.strip(), strict=False)
    if net.num_addresses < 4:
        raise ValueError("Faixa de IP muito pequena")
    return str(net)


def _expiry(v: Optional[str]) -> Optional[str]:
    if not v:
        return None
    v = v.strip()
    if re.fullmatch(r"\d{4}-\d{2}-\d{2}", v):
        return f"{v}T23:59:59+00:00"
    if not re.fullmatch(r"\d{4}-\d{2}-\d{2}T[\d:.]+(\+00:00|Z)?", v):
        raise ValueError("Data inválida (use AAAA-MM-DD)")
    return v


Url = Annotated[str, AfterValidator(_url)]
ReqUrl = Annotated[str, AfterValidator(_req_url)]
IP = Annotated[str, AfterValidator(_ip)]
Text = Annotated[str, Field(max_length=200)]
Name = Annotated[str, Field(min_length=1, max_length=120)]


class Strict(BaseModel):
    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)


class CategoryIn(Strict):
    name: Annotated[str, Field(min_length=1, max_length=60)]
    order: int = Field(0, ge=0, le=9999)


class BannerIn(Strict):
    title: Name
    subtitle: str = Field("", max_length=300)
    image_url: ReqUrl
    content_id: Optional[str] = Field(None, max_length=64)
    order: int = Field(0, ge=0, le=9999)
    active: bool = True


class EpisodeIn(Strict):
    id: Optional[str] = Field(None, max_length=64)
    number: int = Field(ge=1, le=9999)
    title: Name
    synopsis: str = Field("", max_length=2000)
    duration_min: Optional[int] = Field(None, ge=0, le=1000)
    stream_url: ReqUrl
    thumbnail_url: Url = ""


class SeasonIn(Strict):
    number: int = Field(ge=1, le=999)
    title: str = Field("", max_length=120)
    episodes: list[EpisodeIn] = Field(default_factory=list, max_length=500)


class ContentIn(Strict):
    type: Literal["movie", "series"]
    title: Name
    synopsis: str = Field("", max_length=4000)
    year: Optional[int] = Field(None, ge=1900, le=2100)
    duration_min: Optional[int] = Field(None, ge=0, le=1000)
    rating: str = Field("", max_length=10)
    poster_url: Url = ""
    backdrop_url: Url = ""
    stream_url: Url = ""
    category_ids: list[str] = Field(default_factory=list, max_length=50)
    featured: bool = False
    active: bool = True
    license: str = Field("", max_length=200)
    seasons: list[SeasonIn] = Field(default_factory=list, max_length=100)


class ChannelIn(Strict):
    number: int = Field(ge=0, le=99999)
    name: Name
    category: str = Field("", max_length=60)
    logo_url: Url = ""
    stream_url: ReqUrl
    now_playing: str = Field("", max_length=200)
    active: bool = True


class VpnServerIn(Strict):
    name: Name
    country: Annotated[str, Field(min_length=2, max_length=3)]
    protocol: Literal["wireguard", "openvpn"] = "wireguard"
    endpoint: str = Field("", max_length=255, pattern=r"^[A-Za-z0-9.\-:\[\]]*$")
    port: int = Field(51820, ge=1, le=65535)
    public_key: Annotated[str, AfterValidator(_wg_key)] = ""
    address_pool: Annotated[str, AfterValidator(_cidr)] = "10.8.0.0/24"
    dns: list[IP] = Field(default_factory=list, max_length=4)
    allowed_ips: str = Field("0.0.0.0/0, ::/0", max_length=500)
    mtu: Optional[int] = Field(None, ge=1280, le=1500)
    persistent_keepalive: int = Field(25, ge=0, le=300)
    order: int = Field(0, ge=0, le=9999)
    active: bool = True
    management_secret: Optional[str] = Field(None, max_length=500)  # somente escrita, armazenado criptografado


class VpnSettingsIn(Strict):
    enabled: bool = True
    auto_reconnect: bool = True
    kill_switch: bool = True
    dns_leak_protection: bool = True
    dns_servers: list[IP] = Field(default_factory=list, max_length=4)
    default_protocol: Literal["wireguard", "openvpn"] = "wireguard"
    max_devices_per_user: int = Field(3, ge=1, le=20)


class ConfigIn(Strict):
    app_name: Name = "PLAY CHANNELS"
    logo_url: Url = ""
    background_url: Url = ""
    primary_color: str = Field("#D91636", pattern=r"^#[0-9A-Fa-f]{6}$")
    welcome_message: str = Field("", max_length=200)
    maintenance: bool = False
    maintenance_message: str = Field("", max_length=300)
    support_contact: str = Field("", max_length=200)
    vpn: VpnSettingsIn = Field(default_factory=VpnSettingsIn)


class UserCreate(Strict):
    username: Annotated[str, Field(pattern=r"^[a-z0-9._-]{3,40}$")]
    name: str = Field("", max_length=120)
    password: Annotated[str, Field(min_length=6, max_length=128)]
    role: Literal["user", "admin"] = "user"
    active: bool = True
    expires_at: Annotated[Optional[str], AfterValidator(_expiry)] = None
    notes: str = Field("", max_length=500)


class UserUpdate(Strict):
    name: Optional[str] = Field(None, max_length=120)
    password: Optional[str] = Field(None, max_length=128)
    role: Optional[Literal["user", "admin"]] = None
    active: Optional[bool] = None
    expires_at: Annotated[Optional[str], AfterValidator(_expiry)] = None
    notes: Optional[str] = Field(None, max_length=500)


class RenewIn(Strict):
    days: int = Field(ge=1, le=3650)


class ChangePasswordIn(Strict):
    current_password: Annotated[str, Field(min_length=1, max_length=128)]
    new_password: Annotated[str, Field(min_length=1, max_length=128)]


class VpnRegisterIn(Strict):
    server_id: Annotated[str, Field(min_length=1, max_length=64)]
    device_id: Annotated[str, Field(min_length=8, max_length=64, pattern=r"^[A-Za-z0-9-]+$")]
    device_name: str = Field("", max_length=80)
    public_key: Annotated[str, AfterValidator(_wg_key), Field(min_length=44, max_length=44)]


COLLECTION_MODELS = {
    "categories": CategoryIn, "banners": BannerIn, "content": ContentIn,
    "channels": ChannelIn, "vpn_servers": VpnServerIn,
}
