import uuid
import time
import threading
from fastapi import APIRouter, HTTPException, Header, Depends, Request, Response
from fastapi.responses import JSONResponse
from pydantic import BaseModel, EmailStr
from typing import Optional
from app.models.user import get_db_connection
from app.core.auth import (
    hash_password, verify_password, create_token, decode_token, seed_admin,
    COOKIE_NAME, X_AUTH_HEADER,
)

router = APIRouter()

class LoginRequest(BaseModel):
    username: Optional[str] = "admin"
    password: str

class RegisterRequest(BaseModel):
    username: str
    email: str
    password: str

class ChangePasswordRequest(BaseModel):
    old_password: str
    new_password: str

# ── Brute-force guard: in-memory per-IP failed-login limiter ──
_LOGIN_FAILURES = {}          # ip -> [timestamps]
_LOGIN_LOCK = threading.Lock()
MAX_LOGIN_ATTEMPTS = 10
LOGIN_WINDOW_SEC = 900        # 15 min
LOGIN_BLOCK_SEC = 900

def _check_login_ratelimit(ip: str):
    now = time.time()
    with _LOGIN_LOCK:
        stamps = [t for t in _LOGIN_FAILURES.get(ip, []) if now - t < LOGIN_WINDOW_SEC]
        if len(stamps) >= MAX_LOGIN_ATTEMPTS:
            raise HTTPException(status_code=429, detail="Quá nhiều lần đăng nhập thất bại. Vui lòng thử lại sau 15 phút.")
        _LOGIN_FAILURES[ip] = stamps

def _record_login_failure(ip: str):
    now = time.time()
    with _LOGIN_LOCK:
        stamps = _LOGIN_FAILURES.setdefault(ip, [])
        stamps.append(now)
        _LOGIN_FAILURES[ip] = [t for t in stamps if now - t < LOGIN_WINDOW_SEC]

def _record_login_success(ip: str):
    with _LOGIN_LOCK:
        _LOGIN_FAILURES.pop(ip, None)

def _set_auth_cookie(response: Response, token: str):
    response.set_cookie(
        COOKIE_NAME, token,
        max_age=7 * 24 * 3600, httponly=True, samesite="lax",
        # path="/" (default); secure flag set by proxy when behind TLS
    )

def get_current_user(request: Request, authorization: Optional[str] = Header(None), x_auth_token: Optional[str] = Header(None)):
    token = None
    if authorization and authorization.startswith("Bearer "):
        token = authorization.split(" ")[1]
    elif x_auth_token:
        token = x_auth_token
    elif request.cookies.get(COOKIE_NAME):
        token = request.cookies.get(COOKIE_NAME)
    if not token:
        raise HTTPException(status_code=401, detail="Thiếu Token xác thực hoặc Token không hợp lệ")
    payload = decode_token(token)
    if not payload:
        raise HTTPException(status_code=401, detail="Token đã hết hạn hoặc không hợp lệ")
    return payload

def enforce_password_changed(user: dict):
    """Bắt buộc người dùng phải đổi mật khẩu ở lần đăng nhập đầu tiên (22_CLIENT_DESK.md §4.1)."""
    if user.get("must_change_password"):
        raise HTTPException(
            status_code=403,
            detail="Tài khoản bắt buộc phải đổi mật khẩu ở lần đăng nhập đầu tiên trước khi thực hiện xử lý nhạc (HTTP 403 Forbidden)."
        )

@router.post("/login")
async def login(req: LoginRequest, request: Request):
    client_ip = request.client.host if request.client else "unknown"
    _check_login_ratelimit(client_ip)

    conn = get_db_connection()
    cursor = conn.cursor()
    
    username = (req.username or "").strip()
    if not username:
        username = "admin"
        
    password = (req.password or "").strip()
    
    # Case-insensitive search by username or email
    cursor.execute("SELECT * FROM users WHERE LOWER(username) = LOWER(?) OR LOWER(email) = LOWER(?)", (username, username))
    user = cursor.fetchone()
    
    # Auto-heal seed_admin if admin record missing
    if not user and username.lower() == "admin":
        conn.close()
        seed_admin()
        conn = get_db_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM users WHERE username = 'admin'")
        user = cursor.fetchone()

    conn.close()
    
    if not user or not user["is_active"]:
        _record_login_failure(client_ip)
        raise HTTPException(status_code=400, detail="Tài khoản hoặc mật khẩu không chính xác")
        
    if not verify_password(password, user["hashed_password"]):
        _record_login_failure(client_ip)
        raise HTTPException(status_code=400, detail="Tài khoản hoặc mật khẩu không chính xác")
        
    token = create_token(user["id"], user["username"], user["role"], user["must_change_password"])
    _record_login_success(client_ip)

    resp = JSONResponse({
        "access_token": token,
        "user": {
            "id": user["id"],
            "username": user["username"],
            "email": user["email"],
            "role": user["role"],
            "must_change_password": bool(user["must_change_password"])
        }
    })
    _set_auth_cookie(resp, token)
    return resp

def _validate_password_strength(password: str):
    """Minimal strength policy: >= 8 chars and not trivially common."""
    if len(password) < 8:
        raise HTTPException(status_code=400, detail="Mật khẩu phải có ít nhất 8 ký tự")
    lowered = password.lower()
    if lowered in ("admin123", "password", "12345678", "123456789", "qwerty123"):
        raise HTTPException(status_code=400, detail="Mật khẩu quá dễ đoán, vui lòng chọn mật khẩu khác")

@router.post("/register")
async def register(req: RegisterRequest, request: Request):
    username = req.username.strip()
    email = req.email.strip()
    password = req.password.strip()
    _validate_password_strength(password)
    
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute("SELECT id FROM users WHERE LOWER(username) = LOWER(?) OR LOWER(email) = LOWER(?)", (username, email))
    if cursor.fetchone():
        conn.close()
        raise HTTPException(status_code=400, detail="Tên người dùng hoặc Email đã tồn tại")
        
    user_id = str(uuid.uuid4())
    hashed_pwd = hash_password(password)
    now = time.time()
    
    cursor.execute("""
    INSERT INTO users (id, username, email, hashed_password, role, must_change_password, created_at, is_active)
    VALUES (?, ?, ?, ?, 'standard', 0, ?, 1)
    """, (user_id, username, email, hashed_pwd, now))
    
    cursor.execute("""
    INSERT INTO user_quotas (user_id, storage_limit_mb, max_tracks)
    VALUES (?, 500, 16)
    """, (user_id,))
    
    conn.commit()
    conn.close()
    
    token = create_token(user_id, username, "standard", False)
    resp = JSONResponse({
        "access_token": token,
        "user": {
            "id": user_id,
            "username": username,
            "email": email,
            "role": "standard",
            "must_change_password": False
        }
    })
    _set_auth_cookie(resp, token)
    return resp

@router.post("/change-password")
async def change_password(req: ChangePasswordRequest, current_user: dict = Depends(get_current_user)):
    user_id = current_user["user_id"]
    old_pwd = req.old_password.strip()
    new_pwd = req.new_password.strip()
    # Bug #7: register đã validate độ mạnh, change-password thì KHÔNG — admin bị
    # ép đổi mật khẩu có thể đặt `a` (1 ký tự). Áp cùng policy.
    _validate_password_strength(new_pwd)
    
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute("SELECT hashed_password FROM users WHERE id = ?", (user_id,))
    user = cursor.fetchone()
    if not user or not verify_password(old_pwd, user["hashed_password"]):
        conn.close()
        raise HTTPException(status_code=400, detail="Mật khẩu hiện tại không chính xác")
        
    new_hashed = hash_password(new_pwd)
    cursor.execute("""
    UPDATE users SET hashed_password = ?, must_change_password = 0 WHERE id = ?
    """, (new_hashed, user_id))
    
    conn.commit()
    
    cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
    updated_user = cursor.fetchone()
    conn.close()
    
    new_token = create_token(updated_user["id"], updated_user["username"], updated_user["role"], False)
    resp = JSONResponse({
        "message": "Đổi mật khẩu thành công!",
        "access_token": new_token
    })
    _set_auth_cookie(resp, new_token)
    return resp

@router.get("/profile")
async def get_profile(current_user: dict = Depends(get_current_user)):
    user_id = current_user["user_id"]
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute("""
    SELECT u.id, u.username, u.email, u.role, u.must_change_password, q.storage_limit_mb, q.max_tracks
    FROM users u
    LEFT JOIN user_quotas q ON u.id = q.user_id
    WHERE u.id = ?
    """, (user_id,))
    row = cursor.fetchone()
    
    cursor.execute("SELECT SUM(size_bytes) as total_used FROM projects WHERE user_id = ?", (user_id,))
    used_row = cursor.fetchone()
    used_bytes = used_row["total_used"] if used_row and used_row["total_used"] else 0
    used_mb = round(used_bytes / (1024 * 1024), 2)
    
    conn.close()
    
    if not row:
        raise HTTPException(status_code=404, detail="Không tìm thấy thông tin tài khoản")
        
    return {
        "id": row["id"],
        "username": row["username"],
        "email": row["email"],
        "role": row["role"],
        "must_change_password": bool(row["must_change_password"]),
        "quota": {
            "storage_limit_mb": row["storage_limit_mb"] or 500,
            "used_mb": used_mb,
            "max_tracks": row["max_tracks"] or 16
        }
    }


@router.get("/first-time")
async def auth_first_time():
    # "Lần đăng nhập đầu" = tài khoản admin vẫn dùng mật khẩu MẶC ĐỊNH
    # (chưa từng đổi). Sau khi đổi lần đầu → first_time = false → UI xóa
    # gợi ý username/mật khẩu (Tùy chọn - Admin có thể bỏ trống, lần đầu:
    # admin123, nút Điền nhanh).
    try:
        conn = get_db_connection()
        try:
            cur = conn.cursor()
            cur.execute(
                "SELECT id, hashed_password, must_change_password FROM users "
                "WHERE LOWER(role) = 'admin' ORDER BY created_at ASC LIMIT 1"
            )
            row = cur.fetchone()
        finally:
            conn.close()
        if not row:
            return {"first_time": True}
        still_default = False
        try:
            still_default = verify_password("admin123", row["hashed_password"])
        except Exception:
            still_default = False
        return {"first_time": bool(row["must_change_password"]) and still_default}
    except Exception:
        return {"first_time": True}
