import json
import os
import time
from urllib.parse import urlparse


COOKIE_FILENAME = "alibaba_cookies.json"
ALIBABA_DOMAINS = (
    "alibaba.com", ".alibaba.com",
    "message.alibaba.com",
    "alibaba.com.cn", ".alibaba.com.cn",
)
TARGET_COOKIE = "xman_t"
TTL_SECONDS = 7 * 24 * 60 * 60

_VALID_SAME_SITE = {
    "strict": "Strict",
    "lax": "Lax",
    "none": "None",
}


def _get_cookie_path(profile_path):
    return os.path.join(profile_path, COOKIE_FILENAME)


def clear_cookies(profile_path):
    cookie_path = _get_cookie_path(profile_path)
    try:
        os.remove(cookie_path)
    except FileNotFoundError:
        return False
    return True


def _is_number(value):
    return isinstance(value, (int, float)) and not isinstance(value, bool)


def _is_alibaba_domain(domain):
    if not isinstance(domain, str) or not domain:
        return False
    domain = domain.lower()
    return any(domain.endswith(allowed) for allowed in ALIBABA_DOMAINS)


def _is_alibaba_url(url):
    if not isinstance(url, str) or not url:
        return False
    try:
        host = urlparse(url).hostname
    except Exception:
        return False
    return _is_alibaba_domain(host)


def _normalize_same_site(value):
    if not isinstance(value, str):
        return None
    return _VALID_SAME_SITE.get(value.strip().lower())


def _cookie_key(cookie):
    domain = cookie.get("domain")
    if not domain and cookie.get("url"):
        try:
            domain = urlparse(cookie["url"]).hostname
        except Exception:
            domain = None
    return (
        cookie.get("name"),
        domain.lower() if isinstance(domain, str) else domain,
        cookie.get("path"),
    )


def _cookie_for_restore(cookie, now):
    if not isinstance(cookie, dict):
        return None

    name = cookie.get("name")
    value = cookie.get("value")
    if not isinstance(name, str) or not name or not isinstance(value, str):
        return None

    domain = cookie.get("domain")
    url = cookie.get("url")
    path = cookie.get("path")
    has_domain = isinstance(domain, str) and bool(domain)
    has_url_path = isinstance(url, str) and bool(url) and isinstance(path, str) and bool(path)
    if not has_domain and not has_url_path:
        return None

    if has_domain:
        if not _is_alibaba_domain(domain):
            return None
    elif not _is_alibaba_url(url):
        return None

    expires = cookie.get("expires")
    if _is_number(expires):
        if expires == -1:
            expires = None
        elif expires <= now:
            return None
    elif "expires" in cookie:
        expires = None

    restored = {
        "name": name,
        "value": value,
    }
    if has_domain:
        restored["domain"] = domain
        restored["path"] = path if isinstance(path, str) and path.startswith("/") else "/"
    else:
        restored["url"] = url
        restored["path"] = path if isinstance(path, str) and path.startswith("/") else "/"

    if _is_number(expires):
        restored["expires"] = int(expires)

    for attr in ("httpOnly", "secure"):
        if isinstance(cookie.get(attr), bool):
            restored[attr] = cookie[attr]

    same_site = _normalize_same_site(cookie.get("sameSite"))
    if same_site:
        restored["sameSite"] = same_site

    return restored


def _cookie_for_save(cookie, expires_at):
    if not isinstance(cookie, dict):
        return None

    name = cookie.get("name")
    value = cookie.get("value")
    domain = cookie.get("domain")
    if not isinstance(name, str) or not name or not isinstance(value, str):
        return None
    if not _is_alibaba_domain(domain):
        return None

    path = cookie.get("path")
    saved = {
        "name": name,
        "value": value,
        "domain": domain,
        "path": path if isinstance(path, str) and path else "/",
    }

    expires = expires_at if name == TARGET_COOKIE else cookie.get("expires")
    if _is_number(expires) and expires != -1:
        saved["expires"] = int(expires)

    for attr in ("httpOnly", "secure"):
        if isinstance(cookie.get(attr), bool):
            saved[attr] = cookie[attr]

    same_site = _normalize_same_site(cookie.get("sameSite"))
    if same_site:
        saved["sameSite"] = same_site

    return saved


async def load_cookies(ctx, profile_path):
    cookie_path = _get_cookie_path(profile_path)
    if not os.path.exists(cookie_path):
        return

    try:
        with open(cookie_path, "r", encoding="utf-8") as handle:
            payload = json.load(handle)
    except Exception:
        return

    if not isinstance(payload, dict):
        return
    cookies = payload.get("cookies")
    if not isinstance(cookies, list):
        return

    now = time.time()
    try:
        existing_keys = {_cookie_key(cookie) for cookie in await ctx.cookies()}
    except Exception:
        return

    cookies_to_add = []
    for cookie in cookies:
        restored = _cookie_for_restore(cookie, now)
        if not restored:
            continue
        key = _cookie_key(restored)
        if key in existing_keys:
            continue
        existing_keys.add(key)
        cookies_to_add.append(restored)

    if not cookies_to_add:
        return

    try:
        await ctx.add_cookies(cookies_to_add)
    except Exception:
        return


async def save_cookies(ctx, profile_path):
    try:
        current_cookies = await ctx.cookies()
    except Exception:
        return
    if not isinstance(current_cookies, list):
        return

    now = int(time.time())
    expires_at = now + TTL_SECONDS
    cookies = []
    has_target_cookie = False

    for cookie in current_cookies:
        saved = _cookie_for_save(cookie, expires_at)
        if not saved:
            continue
        if saved["name"] == TARGET_COOKIE:
            has_target_cookie = True
        cookies.append(saved)

    if not has_target_cookie:
        return

    payload = {
        "version": 1,
        "saved_at": now,
        "cookies": cookies,
    }

    cookie_path = _get_cookie_path(profile_path)
    temp_path = f"{cookie_path}.{os.getpid()}.tmp"

    try:
        os.makedirs(profile_path, exist_ok=True)
        with open(temp_path, "w", encoding="utf-8") as handle:
            json.dump(payload, handle, ensure_ascii=False, indent=2)
            handle.write("\n")
        os.replace(temp_path, cookie_path)
    except Exception:
        try:
            if os.path.exists(temp_path):
                os.remove(temp_path)
        except Exception:
            pass
