# app/modules/tenancy/routes/api/store_auth.py
"""
Store team authentication endpoints.

Implements dual token storage with path restriction:
- Sets HTTP-only cookie with path=/store (restricted to store routes only)
- Returns token in response for localStorage (API calls)

This prevents:
- Store cookies from being sent to admin routes
- Admin cookies from being sent to store routes
- Cross-context authentication confusion
"""

import logging

from fastapi import APIRouter, Depends, Request, Response
from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.api.deps import get_current_store_api
from app.core.database import get_db
from app.core.environment import should_use_secure_cookies
from app.modules.core.services.auth_service import auth_service
from app.modules.tenancy.exceptions import InvalidCredentialsException
from app.modules.tenancy.models.user_password_reset_token import (
    UserPasswordResetToken,  # noqa: API-007
)
from app.modules.tenancy.schemas.auth import (
    LogoutResponse,
    StoreUserResponse,
    UserContext,
    UserLogin,
)
from app.modules.tenancy.services.user_auth_service import user_auth_service
from middleware.platform_context import get_current_platform
from middleware.store_context import get_current_store

store_auth_router = APIRouter(prefix="/auth")
logger = logging.getLogger(__name__)


# Response model for store login
class StoreLoginResponse(BaseModel):
    access_token: str
    token_type: str
    expires_in: int
    user: dict
    store: dict
    store_role: str
    platform_code: str | None = None


@store_auth_router.post("/login", response_model=StoreLoginResponse)
def store_login(
    user_credentials: UserLogin,
    request: Request,
    response: Response,
    db: Session = Depends(get_db),
):
    """
    Store team member login.

    Authenticates users who are part of a store team.
    Validates against store context if available.

    Sets token in two places:
    1. HTTP-only cookie with path=/store (for browser page navigation)
    2. Response body (for localStorage and API calls)

    Prevents admin users from logging into store portal.
    """
    # Try to get store from middleware first
    store = get_current_store(request)

    # If no store from middleware, try to get from request body
    if not store and hasattr(user_credentials, "store_code"):
        store_code = getattr(user_credentials, "store_code", None)
        if store_code:
            store = auth_service.get_store_by_code(db, store_code)

    # Authenticate user
    login_result = auth_service.login_user(db=db, user_credentials=user_credentials)
    user = login_result["user"]

    # CRITICAL: Prevent admin users from using store login
    if user.is_admin:
        logger.warning(f"Admin user attempted store login: {user.username}")
        raise InvalidCredentialsException(
            "Admins cannot access store portal. Please use admin portal."
        )

    # Determine store and role
    store_role = "Member"

    if store:
        # Check if user has access to this store
        has_access, role = auth_service.get_user_store_role(db, user, store)

        if has_access:
            store_role = role
        else:
            logger.warning(
                f"User {user.username} attempted login to store {store.store_code} "
                f"but is not authorized"
            )
            raise InvalidCredentialsException("You do not have access to this store")
    else:
        # No store context - find which store this user belongs to
        store, store_role = auth_service.find_user_store(user)

        if not store:
            raise InvalidCredentialsException("User is not associated with any store")

    logger.info(
        f"Store team login successful: {user.username} "
        f"for store {store.store_code} as {store_role}"
    )

    # Resolve platform — prefer explicit sources, fall back to store's primary platform
    from app.modules.tenancy.services.platform_service import platform_service

    platform = None

    # Source 1: middleware-detected platform (production domain-based)
    mw_platform = get_current_platform(request)
    if mw_platform and mw_platform.code != "main":
        platform = mw_platform

    # Source 2: platform_code from login body (dev mode — JS sends platform from page context)
    if platform is None and user_credentials.platform_code:
        platform = platform_service.get_platform_by_code_optional(
            db, user_credentials.platform_code
        )
        if not platform:
            raise InvalidCredentialsException(
                f"Unknown platform: {user_credentials.platform_code}"
            )

    # Source 3: fall back to store's primary platform
    if platform is None:
        primary_pid = platform_service.get_first_active_platform_id_for_store(
            db, store.id
        )
        if primary_pid:
            platform = platform_service.get_platform_by_id(db, primary_pid)

    # Verify store-platform link if platform was resolved explicitly (source 1 or 2)
    if platform is not None and (
        mw_platform or user_credentials.platform_code
    ):
        link = platform_service.get_store_platform_entry(
            db, store.id, platform.id
        )
        if not link or not link.is_active:
            raise InvalidCredentialsException(
                f"Store {store.store_code} is not available on platform {platform.code}"
            )

    platform_id = platform.id if platform else None
    platform_code = platform.code if platform else None

    # Create store-scoped access token with store information
    token_data = auth_service.auth_manager.create_access_token(
        user=user,
        store_id=store.id,
        store_code=store.store_code,
        store_role=store_role,
        platform_id=platform_id,
        platform_code=platform_code,
    )

    # Set HTTP-only cookie for browser navigation
    # CRITICAL: path=/store restricts cookie to store routes only
    response.set_cookie(
        key="store_token",
        value=token_data["access_token"],
        httponly=True,  # JavaScript cannot access (XSS protection)
        secure=should_use_secure_cookies(),  # HTTPS only in production/staging
        samesite="lax",  # CSRF protection
        max_age=token_data["expires_in"],  # Match JWT expiry
        path="/store",  # RESTRICTED TO STORE ROUTES ONLY
    )

    logger.debug(
        f"Set store_token cookie with {token_data['expires_in']}s expiry "
        f"(path=/store, httponly=True, secure={should_use_secure_cookies()})"
    )

    # Return full login response with store-scoped token
    return StoreLoginResponse(
        access_token=token_data["access_token"],
        token_type=token_data["token_type"],
        expires_in=token_data["expires_in"],
        user={
            "id": user.id,
            "username": user.username,
            "email": user.email,
            "role": user.role,
            "is_active": user.is_active,
        },
        store={
            "id": store.id,
            "store_code": store.store_code,
            "subdomain": store.subdomain,
            "name": store.name,
            "is_active": store.is_active,
            "is_verified": store.is_verified,
        },
        store_role=store_role,
        platform_code=platform_code,
    )


@store_auth_router.post("/logout", response_model=LogoutResponse)
def store_logout(response: Response):
    """
    Store team member logout.

    Clears the store_token cookie.
    Client should also remove token from localStorage.
    """
    logger.info("Store logout")

    # Clear the cookie (must match path used when setting)
    response.delete_cookie(
        key="store_token",
        path="/store",
    )

    logger.debug("Deleted store_token cookie")

    return LogoutResponse(message="Logged out successfully")


@store_auth_router.get("/me", response_model=StoreUserResponse)
def get_current_store_user(
    user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db)
):
    """
    Get current authenticated store user.

    This endpoint can be called to verify authentication and get user info.
    Requires Authorization header (header-only authentication for API endpoints).
    """
    return StoreUserResponse(
        id=user.id,
        username=user.username,
        email=user.email,
        role=user.role,
        is_active=user.is_active,
        platform_code=user.token_platform_code,
    )


# ============================================================================
# PASSWORD RESET
# ============================================================================


class StoreForgotPasswordRequest(BaseModel):
    email: str


class StoreForgotPasswordResponse(BaseModel):
    message: str


class StoreResetPasswordRequest(BaseModel):
    token: str
    new_password: str


class StoreResetPasswordResponse(BaseModel):
    message: str


@store_auth_router.post("/forgot-password", response_model=StoreForgotPasswordResponse)
def store_forgot_password(
    request: Request,
    body: StoreForgotPasswordRequest,
    db: Session = Depends(get_db),
):
    """
    Request password reset for store team user.

    Sends password reset email if account exists.
    Always returns success to prevent email enumeration.
    """
    user, plaintext_token = user_auth_service.request_password_reset(db, body.email)

    if user and plaintext_token:
        try:
            scheme = "https" if should_use_secure_cookies() else "http"
            host = request.headers.get("host", "localhost:8000")
            # Include store code in reset link if available
            store = get_current_store(request)
            if store:
                reset_link = f"{scheme}://{host}/store/{store.store_code}/reset-password?token={plaintext_token}"
            else:
                reset_link = f"{scheme}://{host}/merchants/reset-password?token={plaintext_token}"

            from app.modules.messaging.services.email_service import EmailService

            email_service = EmailService(db)
            email_service.send_template(
                template_code="merchant_password_reset",
                to_email=user.email,
                to_name=user.username,
                language="en",
                variables={
                    "first_name": user.username,
                    "reset_link": reset_link,
                    "expiry_hours": str(UserPasswordResetToken.TOKEN_EXPIRY_HOURS),
                    "platform_name": "Orion",
                },
                store_id=store.id if store else None,
            )

            db.commit()
            logger.info(f"Password reset email sent to {user.email}")  # noqa: SEC021
        except Exception as e:
            db.rollback()
            logger.error(f"Failed to send password reset email: {e}")  # noqa: SEC021
    else:
        logger.info(f"Password reset requested for non-existent/inactive email {body.email}")  # noqa: SEC021

    return StoreForgotPasswordResponse(
        message="If an account exists with this email, a password reset link has been sent."
    )


@store_auth_router.post("/reset-password", response_model=StoreResetPasswordResponse)
def store_reset_password(
    body: StoreResetPasswordRequest,
    db: Session = Depends(get_db),
):
    """
    Reset store team user password using reset token.

    Validates the token and sets the new password.
    """
    user = user_auth_service.reset_password(db, body.token, body.new_password)
    db.commit()

    logger.info(f"Password reset completed for user {user.id} ({user.email})")  # noqa: SEC021

    return StoreResetPasswordResponse(
        message="Password reset successfully. You can now log in with your new password."
    )
