"""Hashing password (Argon2id), JWT, token perangkat, dan penandatanganan perintah (HMAC-SHA256)."""
from __future__ import annotations

import hashlib
import hmac
import json
import secrets
import time
from datetime import datetime, timedelta, timezone

from argon2 import PasswordHasher
from argon2.exceptions import InvalidHashError, VerifyMismatchError
from jose import JWTError, jwt

from .config import SECRET_KEY, settings

_ph = PasswordHasher()  # Argon2id default
ALGO = "HS256"


def hash_password(pw: str) -> str:
    return _ph.hash(pw)


def verify_password(pw: str, hashed: str) -> bool:
    try:
        return _ph.verify(hashed, pw)
    except (VerifyMismatchError, InvalidHashError):
        return False


def password_strong(pw: str) -> bool:
    return len(pw) >= 8 and any(c.isalpha() for c in pw) and any(c.isdigit() for c in pw)


def sha256(s: str) -> str:
    return hashlib.sha256(s.encode()).hexdigest()


def new_token(nbytes: int = 32) -> str:
    return secrets.token_urlsafe(nbytes)


def create_access_token(sub: str, role: str, uid: int, name: str) -> str:
    exp = datetime.now(timezone.utc) + timedelta(minutes=settings.access_token_minutes)
    return jwt.encode({"sub": sub, "role": role, "uid": uid, "name": name, "exp": exp, "typ": "access"}, SECRET_KEY, ALGO)


def decode_access_token(token: str) -> dict | None:
    try:
        p = jwt.decode(token, SECRET_KEY, algorithms=[ALGO])
    except JWTError:
        return None
    return p if p.get("typ") == "access" else None


# ---- Penandatanganan perintah ke agent -------------------------------------------------
def device_signing_key(device_uid: str) -> str:
    """Kunci HMAC per perangkat, diturunkan dari kunci rahasia server. Dikirim ke agent saat pendaftaran (via HTTPS)."""
    return hmac.new(SECRET_KEY.encode(), f"cmd-sign:{device_uid}".encode(), hashlib.sha256).hexdigest()


def canonical(payload: dict) -> bytes:
    return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()


def sign_command(device_uid: str, payload: dict) -> str:
    return hmac.new(device_signing_key(device_uid).encode(), canonical(payload), hashlib.sha256).hexdigest()


def build_signed_command(device_uid: str, cmd_id: int, action: str, params: dict, ttl: int) -> dict:
    payload = {"id": cmd_id, "action": action, "params": params, "nonce": secrets.token_hex(8), "exp": int(time.time()) + ttl}
    return {**payload, "sig": sign_command(device_uid, payload)}
