from __future__ import annotations

import argparse
import http.cookies
import json
import re
import sys
from pathlib import Path
from typing import Any


COOKIE_ATTRS = {
    "domain",
    "path",
    "expires",
    "max-age",
    "secure",
    "httponly",
    "samesite",
    "priority",
    "partitioned",
}


def clean_domain(value: str) -> str:
    return value.strip().lower().lstrip(".")


def domain_matches(domain: str, target_domain: str) -> bool:
    domain = clean_domain(domain)
    target_domain = clean_domain(target_domain)
    return domain == target_domain or domain.endswith("." + target_domain)


def cookie_pair(name: str, value: str) -> str | None:
    name = name.strip()
    value = value.strip()
    if not name or name.lower() in COOKIE_ATTRS:
        return None
    return f"{name}={value}"


def parse_json_cookies(text: str, target_domain: str) -> list[str]:
    try:
        data = json.loads(text)
    except json.JSONDecodeError:
        return []
    if isinstance(data, dict):
        if isinstance(data.get("cookies"), list):
            data = data["cookies"]
        else:
            data = [data]
    if not isinstance(data, list):
        return []
    pairs = []
    for item in data:
        if not isinstance(item, dict):
            continue
        domain = str(item.get("domain") or item.get("host") or "")
        if domain and not domain_matches(domain, target_domain):
            continue
        pair = cookie_pair(str(item.get("name") or ""), str(item.get("value") or ""))
        if pair:
            pairs.append(pair)
    return pairs


def parse_netscape_cookies(text: str, target_domain: str) -> list[str]:
    pairs = []
    for raw_line in text.splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#") and not line.startswith("#HttpOnly_"):
            continue
        if line.startswith("#HttpOnly_"):
            line = line.removeprefix("#HttpOnly_")
        parts = line.split("\t")
        if len(parts) < 7:
            continue
        domain, _include_subdomains, _path, _secure, _expires, name, value = parts[:7]
        if not domain_matches(domain, target_domain):
            continue
        pair = cookie_pair(name, value)
        if pair:
            pairs.append(pair)
    return pairs


def parse_set_cookie_lines(text: str, target_domain: str) -> list[str]:
    pairs = []
    for raw_line in text.splitlines():
        line = raw_line.strip()
        if not line:
            continue
        line = re.sub(r"^(set-cookie|cookie)\s*:\s*", "", line, flags=re.IGNORECASE)
        if "domain=" in line.lower():
            domain_match = re.search(r"(?:^|;\s*)domain=([^;]+)", line, flags=re.IGNORECASE)
            if domain_match and not domain_matches(domain_match.group(1), target_domain):
                continue
        first = line.split(";", 1)[0].strip()
        if "=" not in first:
            continue
        name, value = first.split("=", 1)
        pair = cookie_pair(name, value)
        if pair:
            pairs.append(pair)
    return pairs


def parse_cookie_header(text: str) -> list[str]:
    normalized = " ".join(line.strip() for line in text.splitlines() if line.strip())
    normalized = re.sub(r"^cookie\s*:\s*", "", normalized, flags=re.IGNORECASE)
    if not normalized:
        return []
    pairs = []
    for part in normalized.split(";"):
        part = part.strip()
        if "=" not in part:
            continue
        name, value = part.split("=", 1)
        pair = cookie_pair(name, value)
        if pair:
            pairs.append(pair)
    return pairs


def dedupe_pairs(pairs: list[str]) -> list[str]:
    by_name: dict[str, str] = {}
    order: list[str] = []
    for pair in pairs:
        name = pair.split("=", 1)[0]
        if name not in by_name:
            order.append(name)
        by_name[name] = pair
    return [by_name[name] for name in order]


def normalize_cookie(text: str, target_domain: str) -> dict[str, Any]:
    raw = text.strip()
    warnings = []
    if not raw:
        return {"ok": False, "cookie": "", "pairs": [], "warnings": ["empty_input"], "errors": ["empty_input"]}
    if "yandex." in raw.lower():
        warnings.append("contains_yandex_marker")

    domain_attrs = re.findall(r"(?:^|;\s*)domain=([^;\r\n]+)", raw, flags=re.IGNORECASE)
    has_domain_attrs = bool(domain_attrs)
    has_target_domain_attr = any(domain_matches(domain, target_domain) for domain in domain_attrs)
    cookie_header_pairs = [] if has_domain_attrs and not has_target_domain_attr else parse_cookie_header(raw)
    parsers = [
        ("json", parse_json_cookies(raw, target_domain)),
        ("netscape", parse_netscape_cookies(raw, target_domain)),
        ("set_cookie", parse_set_cookie_lines(raw, target_domain)),
        ("cookie_header", cookie_header_pairs),
    ]
    best_name, best_pairs = max(parsers, key=lambda item: len(item[1]))
    pairs = dedupe_pairs(best_pairs)
    errors = []
    if not pairs:
        errors.append("no_cookie_pairs_for_target_domain")
    if has_domain_attrs and not has_target_domain_attr:
        errors.append("domain_attributes_do_not_match_target")
    if any(pair.lower().startswith("domain=") for pair in pairs):
        errors.append("domain_attribute_in_cookie_header")
    return {
        "ok": not errors,
        "format": best_name,
        "target_domain": target_domain,
        "cookie": "; ".join(pairs),
        "pairs": pairs,
        "pair_count": len(pairs),
        "warnings": warnings,
        "errors": errors,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Normalize a 1C:ITS cookie input to a Cookie request header.")
    parser.add_argument("--input-file", type=Path)
    parser.add_argument("--target-domain", default="its.1c.ru")
    parser.add_argument("--cookie-only", action="store_true")
    args = parser.parse_args()

    text = args.input_file.read_text(encoding="utf-8-sig") if args.input_file else sys.stdin.read()
    result = normalize_cookie(text, args.target_domain)
    if args.cookie_only:
        if not result["ok"]:
            print("; ".join(result["errors"]), file=sys.stderr)
            return 1
        print(result["cookie"])
        return 0
    print(json.dumps(result, ensure_ascii=False, indent=2))
    return 0 if result["ok"] else 1


if __name__ == "__main__":
    raise SystemExit(main())
