import json
import re

from secure.generators import new_password
from secure.types import ActionResult

_TICKET_URL = "https://account.live.com/password/Change"
_CHANGE_URL = "https://account.live.com/API/ChangePassword"
_UAID = "7a54e2e7491e45feaa0568c19e1ba059"

_TICKET_PATTERNS = (
    r'<input[^>]+id=["\']iPostedTicket["\'][^>]+name=["\']t["\'][^>]+value=["\']([^"\']+)["\']',
    r'<input[^>]+name=["\']t["\'][^>]+value=["\']([^"\']+)["\']',
)


def _parse_ticket(html: str) -> str | None:
    for pat in _TICKET_PATTERNS:
        m = re.search(pat, html or "")
        if m:
            return m.group(1)
    return None


async def _get_password_ticket(http) -> str | None:
    async with http.request("GET", _TICKET_URL) as resp:
        html = await resp.text()
    return _parse_ticket(html)


def _change_payload(ticket: str, password: str, uaid: str) -> dict:
    return {
        "userTicket": ticket,
        "token": None,
        "password": password,
        "expiryEnabled": False,
        "uiflvr": 1001,
        "uaid": uaid,
        "scid": 100104,
        "hpgid": 200710,
    }


def _change_headers(canary: str | None, uaid: str) -> dict:
    return {
        "accept": "application/json",
        "canary": canary or "",
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "hpgid": "200710",
        "scid": "100104",
        "uaid": uaid,
        "uiflvr": "1001",
        "x-requested-with": "XMLHttpRequest",
    }


class PasswordAction:
    name = "password"

    async def run(self, session, ctx) -> ActionResult:
        password = new_password()
        try:
            ticket = await _get_password_ticket(session.http)
            if not ticket:
                return ActionResult(self.name, False, detail="password_ticket_failed")

            async with session.http.request(
                "POST",
                _CHANGE_URL,
                data=json.dumps(_change_payload(ticket, password, _UAID)),
                headers=_change_headers(session.apicanary, _UAID),
            ) as resp:
                if resp.status >= 400:
                    return ActionResult(
                        self.name, False, detail=f"change_password_http_{resp.status}"
                    )
        except Exception as exc:
            ctx.log(f"password action failed for {session.email[:3]}***: {exc}")
            return ActionResult(self.name, False, detail=f"{type(exc).__name__}: {exc}")

        return ActionResult(self.name, True, data={"password": password})
