from sqlalchemy.ext.asyncio import AsyncSession

from app.core.config import settings
from app.core.exceptions import UnauthorizedError, ValidationError
from app.core.security import (
    create_access_token,
    create_refresh_token,
    decode_token,
    hash_password,
    verify_password,
)
from app.models import Admin
from app.repositories import AdminRepository
from app.services.log_service import LogService, LogType


class AuthService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.admins = AdminRepository(session)
        self.logs = LogService(session)

    async def login(self, username: str, password: str) -> dict[str, str]:
        admin = await self.admins.get_by_username(username.strip())
        if not admin or not admin.is_active or not verify_password(password, admin.password_hash):
            raise UnauthorizedError("invalid_credentials")

        await self.logs.write(LogType.ADMIN, "admin_login", f"admin {admin.username} logged in")
        return self._tokens(admin)

    async def refresh(self, refresh_token: str) -> dict[str, str]:
        admin_id = decode_token(refresh_token, "refresh")
        admin = await self.admins.get(admin_id)
        if not admin or not admin.is_active:
            raise UnauthorizedError("invalid_token")
        return self._tokens(admin)

    async def current_admin(self, token: str) -> Admin:
        admin_id = decode_token(token, "access")
        admin = await self.admins.get(admin_id)
        if not admin or not admin.is_active:
            raise UnauthorizedError("invalid_token")
        return admin

    async def change_password(self, admin: Admin, current: str, new_password: str) -> None:
        if not verify_password(current, admin.password_hash):
            raise UnauthorizedError("invalid_credentials")
        if len(new_password) < 8:
            raise ValidationError("password_too_short")
        admin.password_hash = hash_password(new_password)
        await self.session.flush()
        await self.logs.write(LogType.ADMIN, "admin_password_changed", admin.username)

    async def ensure_default_admin(self) -> Admin:
        admin = await self.admins.get_by_username(settings.admin_username)
        if admin:
            return admin
        return await self.admins.create(
            username=settings.admin_username,
            password_hash=hash_password(settings.admin_password),
            role="owner",
        )

    @staticmethod
    def _tokens(admin: Admin) -> dict[str, str]:
        return {
            "access_token": create_access_token(str(admin.id)),
            "refresh_token": create_refresh_token(str(admin.id)),
            "token_type": "bearer",
        }
