import uuid
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional, Tuple

from sqlalchemy.orm import Session as OrmSession

from app.core.errors import AppError
from app.core.security import hash_password
from app.i18n.loader import t
from app.logging.activity_logger import ActivityLogger
from app.models.catalog import UserAccount, Session


class UserService:
    def __init__(self, db: OrmSession) -> None:
        self._db = db
        self._logger = ActivityLogger(db)

    def list_users(self, page: int = 1, per_page: int = 20) -> Tuple[List[UserAccount], int]:
        total = self._db.query(UserAccount).count()
        items = (
            self._db.query(UserAccount)
            .order_by(UserAccount.created_at)
            .offset((page - 1) * per_page)
            .limit(per_page)
            .all()
        )
        return items, total

    def create_user(
        self,
        username: str,
        display_name: str,
        password: str,
        role: str,
        actor_id: str,
        ip: Optional[str] = None,
    ) -> UserAccount:
        existing = self._db.query(UserAccount).filter(UserAccount.username == username).first()
        if existing:
            raise AppError(
                code="user.duplicate_username",
                message=t("user.duplicate_username"),
                status_code=400,
            )

        now = datetime.now(timezone.utc).isoformat()
        user = UserAccount(
            id=str(uuid.uuid4()),
            username=username,
            display_name=display_name,
            password_hash=hash_password(password),
            role=role,
            is_active=1,
            created_at=now,
            updated_at=now,
        )
        self._db.add(user)
        self._db.commit()

        self._logger.log(
            actor_id=actor_id,
            action="user.create",
            resource_type="user",
            resource_id=user.id,
            detail={"username": username, "role": role},
            ip_address=ip,
        )

        return user

    def get_user(self, user_id: str) -> UserAccount:
        user = self._db.query(UserAccount).filter(UserAccount.id == user_id).first()
        if not user:
            raise AppError(
                code="user.not_found",
                message=t("user.not_found"),
                status_code=404,
            )
        return user

    def update_user(
        self,
        user_id: str,
        updates: Dict[str, Any],
        actor_id: str,
        ip: Optional[str] = None,
    ) -> UserAccount:
        user = self.get_user(user_id)
        before: Dict[str, Any] = {}
        after: Dict[str, Any] = {}

        for field in ("display_name", "role", "is_active"):
            if field in updates and updates[field] is not None:
                before[field] = getattr(user, field)
                setattr(user, field, updates[field])
                after[field] = updates[field]

        if "password" in updates and updates["password"] is not None:
            before["password"] = "***"
            user.password_hash = hash_password(updates["password"])
            after["password"] = "***"

        user.updated_at = datetime.now(timezone.utc).isoformat()
        self._db.commit()

        self._logger.log(
            actor_id=actor_id,
            action="user.update",
            resource_type="user",
            resource_id=user_id,
            detail={"before": before, "after": after},
            ip_address=ip,
        )

        return user

    def deactivate_user(
        self,
        user_id: str,
        actor_id: str,
        ip: Optional[str] = None,
    ) -> UserAccount:
        user = self.get_user(user_id)

        # Check if this is the last active admin
        if user.role == "admin":
            admin_count = (
                self._db.query(UserAccount)
                .filter(UserAccount.role == "admin", UserAccount.is_active == 1)
                .count()
            )
            if admin_count <= 1:
                raise AppError(
                    code="user.cannot_deactivate_last_admin",
                    message=t("user.cannot_deactivate_last_admin"),
                    status_code=400,
                )

        user.is_active = 0
        user.updated_at = datetime.now(timezone.utc).isoformat()

        # Delete all sessions for this user
        self._db.query(Session).filter(Session.user_id == user_id).delete()
        self._db.commit()

        self._logger.log(
            actor_id=actor_id,
            action="user.deactivate",
            resource_type="user",
            resource_id=user_id,
            ip_address=ip,
        )

        return user
