from __future__ import annotations

import json
import secrets
import shutil
import string
import uuid
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from .paths import app_data_dir

_EXT_ID = "signup-autofill@proxygod"


def _ext_src_path() -> Path:
    """Locate the signup_extension dir whether running from source or frozen."""
    import sys
    if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
        return Path(sys._MEIPASS) / "proxy_chain_manager" / "signup_extension"
    return Path(__file__).resolve().parent / "signup_extension"




@dataclass(frozen=True)
class SignupSitePreset:
    key: str
    label: str
    url: str
    hosts: tuple[str, ...]
    extra_selectors: dict[str, list[str]] = field(default_factory=dict)


SIGNUP_PRESETS: dict[str, SignupSitePreset] = {
    "google": SignupSitePreset(
        key="google",
        label="Google",
        url="https://accounts.google.com/signup/v2/createaccount?flowName=GlifWebSignIn&flowEntry=SignUp",
        hosts=("accounts.google.com", "google.com"),
        extra_selectors={
            "first_name": ['input[name="firstName"]'],
            "last_name": ['input[name="lastName"]'],
            "email": ['input[name="Username"]', 'input[type="email"]'],
            "password": ['input[name="Passwd"]'],
            "password_confirm": ['input[name="PasswdAgain"]'],
        },
    ),
    "protonmail": SignupSitePreset(
        key="protonmail",
        label="Proton Mail",
        url="https://account.proton.me/signup",
        hosts=("account.proton.me", "proton.me"),
        extra_selectors={
            "email": ['input[id="email"]', 'input[name="email"]', 'input[type="email"]'],
            "password": ['input[id="password"]', 'input[name="password"]'],
            "password_confirm": ['input[id="password-confirm"]', 'input[name="passwordConfirmation"]'],
        },
    ),
    "hydraproxy": SignupSitePreset(
        key="hydraproxy",
        label="HydraProxy",
        url="https://dashboard.hydraproxy.com/register",
        hosts=("dashboard.hydraproxy.com", "hydraproxy.com"),
        extra_selectors={
            "email": ['input[name="email"]', 'input[type="email"]', 'input[id="email"]'],
            "password": ['input[name="password"]', 'input[type="password"]'],
            "password_confirm": ['input[name="password_confirmation"]', 'input[name="confirmPassword"]'],
            "username": ['input[name="username"]', 'input[name="name"]'],
        },
    ),
    "outlook": SignupSitePreset(
        key="outlook",
        label="Outlook / Microsoft",
        url="https://signup.live.com/signup",
        hosts=("signup.live.com", "login.live.com", "account.microsoft.com"),
        extra_selectors={
            "email": ['input[name="MemberName"]', 'input[type="email"]'],
            "password": ['input[name="Password"]', 'input[name="PasswordInput"]'],
            "first_name": ['input[name="FirstName"]'],
            "last_name": ['input[name="LastName"]'],
        },
    ),
    "github": SignupSitePreset(
        key="github",
        label="GitHub",
        url="https://github.com/signup",
        hosts=("github.com",),
        extra_selectors={
            "email": ['input[name="user[email]"]', 'input[autocomplete="email"]'],
            "password": ['input[name="user[password]"]', 'input[autocomplete="new-password"]'],
            "username": ['input[name="user[login]"]'],
        },
    ),
    "reddit": SignupSitePreset(
        key="reddit",
        label="Reddit",
        url="https://www.reddit.com/register",
        hosts=("www.reddit.com", "reddit.com"),
        extra_selectors={
            "email": ['input[name="email"]', 'input[type="email"]'],
            "username": ['input[name="username"]'],
            "password": ['input[name="password"]'],
        },
    ),
    "discord": SignupSitePreset(
        key="discord",
        label="Discord",
        url="https://discord.com/register",
        hosts=("discord.com",),
        extra_selectors={
            "email": ['input[name="email"]', 'input[type="email"]'],
            "username": ['input[name="username"]'],
            "password": ['input[name="password"]'],
        },
    ),
    "x_twitter": SignupSitePreset(
        key="x_twitter",
        label="X / Twitter",
        url="https://x.com/i/flow/signup",
        hosts=("x.com", "twitter.com"),
        extra_selectors={
            "email": ['input[name="email"]', 'input[type="email"]'],
            "username": ['input[name="username"]'],
            "password": ['input[name="password"]'],
            "first_name": ['input[name="name"]'],
        },
    ),
    "telegram": SignupSitePreset(
        key="telegram",
        label="Telegram Web",
        url="https://web.telegram.org/",
        hosts=("web.telegram.org", "telegram.org"),
    ),
    "tutanota": SignupSitePreset(
        key="tutanota",
        label="Tuta Mail",
        url="https://app.tuta.com/signup",
        hosts=("app.tuta.com", "tutanota.com", "tuta.com"),
        extra_selectors={
            "email": ['input[id="mailAddress"]', 'input[name="mailAddress"]'],
            "password": ['input[id="password1"]', 'input[type="password"]'],
            "password_confirm": ['input[id="password2"]'],
        },
    ),
    "mailcom": SignupSitePreset(
        key="mailcom",
        label="mail.com",
        url="https://signup.mail.com/",
        hosts=("signup.mail.com", "mail.com"),
        extra_selectors={
            "username": ['input[name="email_user"]', 'input[name="username"]'],
            "password": ['input[name="password"]'],
            "first_name": ['input[name="firstName"]'],
            "last_name": ['input[name="lastName"]'],
        },
    ),
}


# Public site-key list in display order. Useful for building dropdowns.
SIGNUP_PRESET_KEYS: tuple[str, ...] = (
    "google", "protonmail", "outlook", "tutanota", "mailcom",
    "github", "reddit", "discord", "x_twitter", "telegram",
    "hydraproxy",
)


_FIRST_NAMES: tuple[str, ...] = (
    "Alex", "Jordan", "Casey", "Morgan", "Taylor", "Riley", "Cameron",
    "Sam", "Jamie", "Quinn", "Avery", "Drew", "Reese", "Skyler", "Logan",
    "Hayden", "Parker", "Rowan", "Sage", "Emerson", "Finley", "Harper",
    "Kai", "Micah", "Nico", "Phoenix", "River", "Shawn", "Sutton", "Wren",
)

_LAST_NAMES: tuple[str, ...] = (
    "Reed", "Hayes", "Brooks", "Bennett", "Carter", "Foster", "Gray",
    "Hughes", "Jensen", "Knight", "Lambert", "Mason", "Nash", "Owens",
    "Porter", "Quincy", "Reyes", "Sutton", "Tate", "Underwood", "Vance",
    "Walsh", "Yates", "Zimmerman", "Abbott", "Barker", "Cohen", "Dixon",
    "Ellis", "Fitch",
)


def random_first_name() -> str:
    return secrets.choice(_FIRST_NAMES)


def random_last_name() -> str:
    return secrets.choice(_LAST_NAMES)


def random_full_name() -> tuple[str, str]:
    return random_first_name(), random_last_name()


def random_username(first: str = "", last: str = "") -> str:
    """Generate a plausible-looking username.

    If a name is provided we mix it with digits; otherwise we use two short
    name fragments. Always lowercase, always 8-18 chars.
    """
    f = (first or random_first_name()).lower()
    l = (last or random_last_name()).lower()
    seps = ("", ".", "_", "")
    sep = secrets.choice(seps)
    digits = "".join(secrets.choice(string.digits) for _ in range(secrets.choice([2, 3, 4])))
    style = secrets.randbelow(4)
    if style == 0:
        base = f + sep + l + digits
    elif style == 1:
        base = f + digits
    elif style == 2:
        base = l + sep + f[0] + digits
    else:
        base = f[0] + sep + l + digits
    base = base.strip(".-_")
    return base[:18]


@dataclass
class SignupDraft:
    site_key: str = "google"
    custom_url: str = ""
    email: str = ""
    password: str = ""
    username: str = ""
    first_name: str = ""
    last_name: str = ""
    notes: str = ""


@dataclass
class AccountRecord:
    id: str
    site: str
    url: str
    email: str
    password: str
    username: str = ""
    first_name: str = ""
    last_name: str = ""
    exit_ip: str = ""
    exit_hop: str = ""
    notes: str = ""
    status: str = "created"  # pending | created
    created_at: str = ""


def accounts_path() -> Path:
    return app_data_dir() / "signup_accounts.json"


def draft_path() -> Path:
    return app_data_dir() / "signup_draft.json"


def generate_password(length: int = 18) -> str:
    n = max(12, min(64, int(length)))
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*-_=+"
    while True:
        pw = "".join(secrets.choice(alphabet) for _ in range(n))
        if (
            any(c.islower() for c in pw)
            and any(c.isupper() for c in pw)
            and any(c.isdigit() for c in pw)
        ):
            return pw


def preset_for(key: str) -> SignupSitePreset | None:
    return SIGNUP_PRESETS.get(key)


def resolve_signup_url(draft: SignupDraft) -> str:
    if draft.site_key == "custom":
        return (draft.custom_url or "").strip()
    p = preset_for(draft.site_key)
    return p.url if p else ""


def load_draft() -> SignupDraft:
    from . import secrets_store as _ss
    p = draft_path()
    if not p.is_file():
        return SignupDraft()
    try:
        raw = json.loads(p.read_text(encoding="utf-8"))
        kwargs = {k: raw.get(k, "") for k in SignupDraft.__dataclass_fields__}
        if kwargs.get("password"):
            kwargs["password"] = _ss.decrypt(kwargs["password"])
        return SignupDraft(**kwargs)
    except (OSError, json.JSONDecodeError, TypeError):
        return SignupDraft()


def save_draft(draft: SignupDraft) -> None:
    from . import secrets_store as _ss
    d = asdict(draft)
    if d.get("password"):
        d["password"] = _ss.encrypt(d["password"])
    draft_path().write_text(json.dumps(d, indent=2), encoding="utf-8")


def load_accounts() -> list[AccountRecord]:
    from . import secrets_store as _ss
    p = accounts_path()
    if not p.is_file():
        return []
    try:
        raw = json.loads(p.read_text(encoding="utf-8"))
        if not isinstance(raw, list):
            return []
        out: list[AccountRecord] = []
        for item in raw:
            if not isinstance(item, dict):
                continue
            fields = AccountRecord.__dataclass_fields__
            kwargs = {k: item.get(k, "") for k in fields}
            if not kwargs.get("id"):
                kwargs["id"] = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S%f")
            # Transparent DPAPI decrypt; plaintext passes through.
            if kwargs.get("password"):
                kwargs["password"] = _ss.decrypt(kwargs["password"])
            out.append(AccountRecord(**kwargs))
        return out
    except (OSError, json.JSONDecodeError, TypeError):
        return []


def save_accounts(rows: list[AccountRecord]) -> None:
    from . import secrets_store as _ss
    serialized = []
    for r in rows:
        d = asdict(r)
        # Encrypt sensitive fields at rest using Windows DPAPI when available.
        # Legacy plaintext entries are migrated transparently on first save.
        if d.get("password"):
            d["password"] = _ss.encrypt(d["password"])
        serialized.append(d)
    accounts_path().write_text(
        json.dumps(serialized, indent=2),
        encoding="utf-8",
    )


def append_account(record: AccountRecord) -> None:
    rows = load_accounts()
    rows.insert(0, record)
    save_accounts(rows)


def build_autofill_config(draft: SignupDraft) -> dict[str, Any]:
    preset = preset_for(draft.site_key)
    hosts = list(preset.hosts) if preset else []
    selectors: dict[str, list[str]] = {}
    if preset:
        selectors.update(preset.extra_selectors)

    fields: dict[str, str] = {}
    if draft.first_name:
        fields["first_name"] = draft.first_name
    if draft.last_name:
        fields["last_name"] = draft.last_name
    if draft.username:
        fields["username"] = draft.username
    if draft.email:
        fields["email"] = draft.email
    if draft.password:
        fields["password"] = draft.password
        fields["password_confirm"] = draft.password

    out_fields: dict[str, Any] = {}
    for key, val in fields.items():
        out_fields[key] = val
        sel_key = f"{key}_selectors"
        if key in selectors:
            out_fields[sel_key] = selectors[key]

    return {
        "site": draft.site_key,
        "hosts": hosts,
        "fields": out_fields,
        "updated_at": datetime.now(timezone.utc).isoformat(),
    }


def _extensions_json_entry(ext_dir_name: str) -> dict[str, Any]:
    now = int(datetime.now(timezone.utc).timestamp()) * 1000
    return {
        "id": _EXT_ID,
        "syncGUID": str(uuid.uuid4()),
        "version": "1.0.0",
        "type": "extension",
        "location": {
            "appProfile": {
                "root": "app-profile",
                "path": f"extensions/{ext_dir_name}",
            }
        },
        "internalName": None,
        "updateURL": None,
        "webExtensionConverted": False,
        "webExtensionId": _EXT_ID,
        "active": True,
        "userDisabled": False,
        "appDisabled": False,
        "installDate": now,
        "scope": 1,
    }


def install_signup_extension(profile_dir: Path, draft: SignupDraft) -> None:
    """Copy autofill WebExtension into the Firefox profile and write current field values."""
    profile_dir.mkdir(parents=True, exist_ok=True)
    ext_name = _EXT_ID
    dest = profile_dir / "extensions" / ext_name
    if dest.exists():
        shutil.rmtree(dest, ignore_errors=True)
    shutil.copytree(_ext_src_path(), dest)

    cfg = build_autofill_config(draft)
    (dest / "autofill_config.json").write_text(json.dumps(cfg, indent=2), encoding="utf-8")

    ext_json_path = profile_dir / "extensions.json"
    entry = _extensions_json_entry(ext_name)
    data: dict[str, Any]
    if ext_json_path.is_file():
        try:
            data = json.loads(ext_json_path.read_text(encoding="utf-8"))
        except (OSError, json.JSONDecodeError):
            data = {"schemaVersion": 35, "addons": []}
    else:
        data = {"schemaVersion": 35, "addons": []}

    addons = data.get("addons")
    if not isinstance(addons, list):
        addons = []
    addons = [a for a in addons if isinstance(a, dict) and a.get("id") != _EXT_ID]
    addons.append(entry)
    data["addons"] = addons
    ext_json_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
