"""
reset_lockouts.py

Purpose:
  Administrative utility to reset active login lockouts across all users and
  IPs. Intended for emergency or support use when legitimate users are blocked
  by the rate limiter.

How it works:
  - Connects to the SQLite database used by the app (path from
    `app_modules.paths.DB_PATH`, which itself honors the `DATABASE_PATH`
    environment variable in production).
  - Ensures the required tables exist for safety (no-ops if they already do).
  - Clears lock state only for currently locked records (where
    `locked_until > now()`), and resets their counters so they don't
    immediately re-lock.

Security notes:
  - This script should be run by a trusted operator on the server where the
    database resides. It does not require the Flask app to be running.
  - It only affects rate-limit state; it does not alter user credentials or
    authorization data.
"""

from __future__ import annotations

import time
import sqlite3
from typing import Tuple

from app_modules.paths import DB_PATH


def _ensure_tables(conn: sqlite3.Connection) -> None:
    cur = conn.cursor()
    # Per-username+IP attempts
    cur.execute(
        """
        CREATE TABLE IF NOT EXISTS login_attempts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT NOT NULL,
            ip TEXT NOT NULL,
            fail_count INTEGER NOT NULL DEFAULT 0,
            last_failed_at INTEGER NOT NULL DEFAULT 0,
            locked_until INTEGER NOT NULL DEFAULT 0,
            UNIQUE(username, ip)
        )
        """
    )
    # IP-only attempts
    cur.execute(
        """
        CREATE TABLE IF NOT EXISTS login_ip_attempts (
            ip TEXT PRIMARY KEY,
            fail_count INTEGER NOT NULL DEFAULT 0,
            last_failed_at INTEGER NOT NULL DEFAULT 0,
            locked_until INTEGER NOT NULL DEFAULT 0
        )
        """
    )
    conn.commit()


def reset_active_lockouts(conn: sqlite3.Connection) -> Tuple[int, int]:
    now = int(time.time())
    cur = conn.cursor()
    cur.execute(
        "UPDATE login_attempts SET fail_count = 0, last_failed_at = 0, locked_until = 0 WHERE locked_until > ?",
        (now,),
    )
    users_cleared = cur.rowcount if cur.rowcount is not None else 0

    cur.execute(
        "UPDATE login_ip_attempts SET fail_count = 0, last_failed_at = 0, locked_until = 0 WHERE locked_until > ?",
        (now,),
    )
    ips_cleared = cur.rowcount if cur.rowcount is not None else 0
    conn.commit()
    return users_cleared, ips_cleared


def main() -> None:
    conn = sqlite3.connect(DB_PATH)
    try:
        _ensure_tables(conn)
        users, ips = reset_active_lockouts(conn)
        print(f"Cleared active lockouts -> users: {users}, ips: {ips}\nDB: {DB_PATH}")
    finally:
        conn.close()


if __name__ == '__main__':
    main()


