import hashlib
import secrets
from datetime import datetime, timedelta
from typing import Optional

import db

PBKDF2_ROUNDS = 200_000
SESSION_DAYS = 1


def hash_password(password: str, salt: Optional[str] = None) -> str:
    salt = salt or secrets.token_hex(16)
    digest = hashlib.pbkdf2_hmac(
        "sha256", password.encode(), bytes.fromhex(salt), PBKDF2_ROUNDS
    ).hex()
    return f"{salt}${digest}"


def verify_password(password: str, stored: str) -> bool:
    try:
        salt, _ = stored.split("$", 1)
    except ValueError:
        return False
    return secrets.compare_digest(hash_password(password, salt), stored)


def create_session(admin_id: int) -> str:
    sid = secrets.token_urlsafe(32)
    now = db.now_str()
    expires = (datetime.now() + timedelta(days=SESSION_DAYS)).strftime("%Y-%m-%d %H:%M:%S")
    with db.get_db() as conn:
        conn.execute(
            "INSERT INTO session (id, admin_id, created_at, expires_at) VALUES (?, ?, ?, ?)",
            (sid, admin_id, now, expires),
        )
        conn.execute("DELETE FROM session WHERE expires_at < ?", (now,))
    return sid


def get_session_admin(sid):
    if not sid:
        return None
    with db.get_db() as conn:
        row = conn.execute(
            """SELECT a.id, a.username, a.role, a.approval_status, a.approval_note, a.unit_dept
               FROM session s
               JOIN admin a ON a.id = s.admin_id
               WHERE s.id = ? AND s.expires_at > ? AND a.disabled = 0""",
            (sid, db.now_str()),
        ).fetchone()
    return dict(row) if row else None


def destroy_session(sid: str):
    with db.get_db() as conn:
        conn.execute("DELETE FROM session WHERE id = ?", (sid,))


def authenticate(username: str, password: str):
    with db.get_db() as conn:
        row = conn.execute(
            "SELECT id, username, password_hash, role, disabled, approval_status FROM admin WHERE username = ?",
            (username,),
        ).fetchone()
    if row and not row["disabled"] and verify_password(password, row["password_hash"]):
        return {"id": row["id"], "username": row["username"], "role": row["role"],
                "approval_status": row["approval_status"]}
    return None


def change_password(admin_id: int, new_password: str):
    with db.get_db() as conn:
        conn.execute(
            "UPDATE admin SET password_hash = ? WHERE id = ?",
            (hash_password(new_password), admin_id),
        )
        conn.execute("DELETE FROM session WHERE admin_id = ?", (admin_id,))
