"""Проверка initData Telegram Mini App и сессии панели."""

from __future__ import annotations

import hashlib
import hmac
import json
import time
from urllib.parse import parse_qsl, unquote


def _field_sets(raw: str) -> list[dict]:
    sets: list[dict] = []
    qsl = dict(parse_qsl(raw, keep_blank_values=True))
    if qsl:
        sets.append(qsl)
    manual: dict[str, str] = {}
    for part in str(raw).split("&"):
        if "=" not in part:
            continue
        key, value = part.split("=", 1)
        manual[unquote(key)] = unquote(value)
    if manual and manual != qsl:
        sets.append(manual)
    return sets or [qsl]


def _user_from_fields(fields: dict) -> dict:
    try:
        user = json.loads(fields.get("user") or "")
    except (TypeError, ValueError, json.JSONDecodeError) as exc:
        raise ValueError("Нет данных пользователя") from exc
    if not isinstance(user, dict) or not user.get("id"):
        raise ValueError("Нет данных пользователя")
    user["id"] = int(user["id"])
    return user


def extract_user_from_init_data(init_data: str) -> dict:
    raw = str(init_data or "").strip()
    if not raw:
        raise ValueError("Откройте мини-приложение из бота")
    last_error: Exception = ValueError("Нет данных пользователя")
    for fields in _field_sets(raw):
        try:
            return _user_from_fields(fields)
        except ValueError as exc:
            last_error = exc
    raise last_error


def validate_init_data(init_data: str, bot_token: str, *, max_age: int = 7 * 86400) -> dict:
    raw = str(init_data or "").strip()
    if not raw:
        raise ValueError("Откройте мини-приложение из бота")
    last_error: Exception = ValueError("Неверная подпись Telegram")
    for fields in _field_sets(raw):
        parsed = dict(fields)
        received_hash = str(parsed.pop("hash", "") or "")
        if not received_hash:
            last_error = ValueError("Нет подписи Telegram")
            continue
        data_check = "\n".join(f"{key}={value}" for key, value in sorted(parsed.items()))
        secret = hmac.new(b"WebAppData", bot_token.encode("utf-8"), hashlib.sha256).digest()
        calculated = hmac.new(secret, data_check.encode("utf-8"), hashlib.sha256).hexdigest()
        if not hmac.compare_digest(calculated, received_hash):
            last_error = ValueError("Неверная подпись Telegram")
            continue
        try:
            auth_date = int(parsed.get("auth_date") or 0)
        except (TypeError, ValueError):
            auth_date = 0
        if auth_date <= 0:
            last_error = ValueError("Сессия мини-приложения устарела, откройте его заново")
            continue
        age = time.time() - auth_date
        if age > max_age or age < -300:
            last_error = ValueError("Сессия мини-приложения устарела, откройте его заново")
            continue
        return _user_from_fields(fields)
    raise last_error


def validate_init_data_any(init_data: str, tokens: list[str], *, max_age: int = 7 * 86400) -> dict:
    raw = str(init_data or "").strip()
    if not raw:
        raise ValueError("Откройте мини-приложение из бота")
    last_error: Exception = ValueError("Неверная подпись Telegram")
    seen: set[str] = set()
    for token in tokens:
        token = str(token or "").strip()
        if not token or token in seen:
            continue
        seen.add(token)
        try:
            return validate_init_data(raw, token, max_age=max_age)
        except ValueError as exc:
            last_error = exc
            if str(exc).startswith("Неверная подпись") or str(exc).startswith("Нет подписи"):
                continue
            raise
    raise last_error


def make_panel_token(user_id: int, bot_token: str, *, ttl: int = 7 * 86400) -> str:
    exp = int(time.time()) + int(ttl)
    payload = f"{int(user_id)}.{exp}"
    sig = hmac.new(bot_token.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest()[:24]
    return f"{payload}.{sig}"


def parse_panel_token(token: str, bot_token: str) -> int:
    parts = str(token or "").split(".")
    if len(parts) != 3:
        raise ValueError("Сессия недействительна")
    user_id, exp, sig = parts
    payload = f"{user_id}.{exp}"
    expect = hmac.new(bot_token.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest()[:24]
    if not hmac.compare_digest(expect, sig):
        raise ValueError("Сессия недействительна")
    try:
        if int(exp) < time.time():
            raise ValueError("Сессия устарела, откройте панель заново")
        return int(user_id)
    except (TypeError, ValueError) as exc:
        raise ValueError("Сессия недействительна") from exc
