import random
import string
import uuid
from datetime import datetime, timezone

from bson.objectid import ObjectId
from pymongo import MongoClient

from controller.mail_helper import MailHelper
from MongoDBConnection import DB_NAME, MONGO_URI
from model.access_token_model import AccessTokenModel
from utils.password_helper import hash_password, verify_password

client = MongoClient(MONGO_URI)
db = client[DB_NAME]
collection = db["users"]


class StaffModel:
    @staticmethod
    def generate_random_password(length=8):
        characters = string.ascii_letters + string.digits
        return ''.join(random.choice(characters) for _ in range(length))

    @staticmethod
    def _staff_query(staff_id: str):
        try:
            return {"_id": ObjectId(staff_id), "role": "staff"}
        except Exception:
            return {"id": staff_id, "role": "staff"}

    @classmethod
    def create_staff(cls, name: str, email: str, permissions: list):
        existing_user = collection.find_one({"email": email})
        if existing_user:
            raise Exception("Email đã tồn tại trong hệ thống!")

        raw_password = cls.generate_random_password(8)
        staff_id = str(uuid.uuid4())
        created_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")

        mail_data = {
            "username": name,
            "password": raw_password,
            "note": "Đây là tài khoản Staff của bạn. Hãy đổi mật khẩu sau khi đăng nhập.",
        }

        new_staff = {
            "id": staff_id,
            "name": name,
            "email": email,
            "password": hash_password(raw_password),
            "role": "staff",
            "status": "active",
            "permissions": permissions,
            "createdAt": created_at,
        }

        MailHelper.send_new_password_email(email, mail_data)
        collection.insert_one(new_staff)

        if "_id" in new_staff:
            new_staff["_id"] = str(new_staff["_id"])

        # Không trả hash password ra frontend.
        new_staff.pop("password", None)
        return new_staff

    @staticmethod
    def check_access(user_permissions: list, required_permission: str):
        if "admin" in user_permissions:
            return True

        return required_permission in user_permissions

    @classmethod
    def change_password(cls, staff_id: str, old_password: str, new_password: str):
        query = cls._staff_query(staff_id)
        staff = collection.find_one(query)

        if not staff:
            return False, "Tài khoản không tồn tại trên hệ thống!"

        if not verify_password(old_password, staff.get("password", "")):
            return False, "Mật khẩu hiện tại không chính xác!"

        collection.update_one(query, {"$set": {"password": hash_password(new_password)}})
        AccessTokenModel.revoke_all_for_actor(str(staff.get("_id") or staff_id))
        return True, "Đổi mật khẩu thành công, vui lòng đăng nhập lại!"

    @classmethod
    def update_staff(cls, staff_id: str, email: str, status: str, permissions: list):
        query = cls._staff_query(staff_id)

        result = collection.update_one(
            query,
            {"$set": {
                "email": email,
                "status": status,
                "permissions": permissions,
            }},
        )

        if result.matched_count == 0:
            return False, "Không tìm thấy nhân viên!"

        return True, "Cập nhật nhân viên thành công!"

    @classmethod
    def admin_reset_password(cls, staff_id: str):
        query = cls._staff_query(staff_id)
        staff = collection.find_one(query)
        if not staff:
            return False, "Không tìm thấy nhân viên!"

        raw_password = cls.generate_random_password(8)
        collection.update_one(query, {"$set": {"password": hash_password(raw_password)}})
        AccessTokenModel.revoke_all_for_actor(str(staff.get("_id") or staff_id))

        mail_data = {
            "username": staff.get("name", "Staff"),
            "password": raw_password,
            "note": "Mật khẩu của bạn vừa được Admin đặt lại. Hãy đổi mật khẩu ngay sau khi đăng nhập.",
        }
        try:
            MailHelper.send_new_password_email(staff["email"], mail_data)
        except Exception as e:
            print(f"Lỗi gửi mail: {e}")

        return True, f"Đã tạo mật khẩu mới và gửi email tới {staff['email']}!"

    @classmethod
    def forgot_password_by_email(cls, email: str):
        staff = collection.find_one({"email": email, "role": "staff"})
        if not staff:
            raise ValueError("Email này chưa được đăng ký trong hệ thống.")
        raw_password = cls.generate_random_password(8)
        collection.update_one(
            {"_id": staff["_id"]},
            {"$set": {"password": hash_password(raw_password)}}
        )
        AccessTokenModel.revoke_all_for_actor(str(staff["_id"]))
        mail_data = {
            "username": staff.get("name", "Staff"),
            "password": raw_password,
            "note": "Bạn vừa yêu cầu cấp lại mật khẩu. Hãy đổi mật khẩu sau khi đăng nhập.",
        }
        MailHelper.send_new_password_email(staff["email"], mail_data)