# app/api/v1/shop/auth.py
"""
Shop Authentication API (Public)

Public endpoints for customer authentication in shop frontend.
Uses vendor from request.state (injected by VendorContextMiddleware).

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

This prevents:
- Customer cookies from being sent to admin or vendor 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.core.database import get_db
from app.core.environment import should_use_secure_cookies
from app.exceptions import VendorNotFoundException
from app.services.customer_service import customer_service
from models.schema.auth import (
    LogoutResponse,
    PasswordResetRequestResponse,
    PasswordResetResponse,
    UserLogin,
)
from models.schema.customer import CustomerRegister, CustomerResponse

router = APIRouter()
logger = logging.getLogger(__name__)


# Response model for customer login
class CustomerLoginResponse(BaseModel):
    """Customer login response with token and customer data."""

    access_token: str
    token_type: str
    expires_in: int
    user: CustomerResponse  # Use CustomerResponse instead of UserResponse


@router.post("/auth/register", response_model=CustomerResponse)
def register_customer(
    request: Request, customer_data: CustomerRegister, db: Session = Depends(get_db)
):
    """
    Register a new customer for current vendor.

    Vendor is automatically determined from request context.
    Customer accounts are vendor-scoped - each vendor has independent customers.
    Same email can be used for different vendors.

    Request Body:
    - email: Customer email address
    - password: Customer password
    - first_name: Customer first name
    - last_name: Customer last name
    - phone: Customer phone number (optional)
    """
    # Get vendor from middleware
    vendor = getattr(request.state, "vendor", None)

    if not vendor:
        raise VendorNotFoundException("context", identifier_type="subdomain")

    logger.debug(
        f"[SHOP_API] register_customer for vendor {vendor.subdomain}",
        extra={
            "vendor_id": vendor.id,
            "vendor_code": vendor.subdomain,
            "email": customer_data.email,
        },
    )

    # Create customer account
    customer = customer_service.register_customer(
        db=db, vendor_id=vendor.id, customer_data=customer_data
    )
    db.commit()

    logger.info(
        f"New customer registered: {customer.email} for vendor {vendor.subdomain}",
        extra={
            "customer_id": customer.id,
            "vendor_id": vendor.id,
            "email": customer.email,
        },
    )

    return CustomerResponse.model_validate(customer)


@router.post("/auth/login", response_model=CustomerLoginResponse)
def customer_login(
    request: Request,
    user_credentials: UserLogin,
    response: Response,
    db: Session = Depends(get_db),
):
    """
    Customer login for current vendor.

    Vendor is automatically determined from request context.
    Authenticates customer and returns JWT token.
    Customer must belong to the specified vendor.

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

    The cookie is restricted to /shop/* routes only to prevent
    it from being sent to admin or vendor routes.

    Request Body:
    - email_or_username: Customer email or username
    - password: Customer password
    """
    # Get vendor from middleware
    vendor = getattr(request.state, "vendor", None)

    if not vendor:
        raise VendorNotFoundException("context", identifier_type="subdomain")

    logger.debug(
        f"[SHOP_API] customer_login for vendor {vendor.subdomain}",
        extra={
            "vendor_id": vendor.id,
            "vendor_code": vendor.subdomain,
            "email_or_username": user_credentials.email_or_username,
        },
    )

    # Authenticate customer
    login_result = customer_service.login_customer(
        db=db, vendor_id=vendor.id, credentials=user_credentials
    )

    logger.info(
        f"Customer login successful: {login_result['customer'].email} for vendor {vendor.subdomain}",
        extra={
            "customer_id": login_result["customer"].id,
            "vendor_id": vendor.id,
            "email": login_result["customer"].email,
        },
    )

    # Calculate cookie path based on vendor access method
    vendor_context = getattr(request.state, "vendor_context", None)
    access_method = (
        vendor_context.get("detection_method", "unknown")
        if vendor_context
        else "unknown"
    )

    cookie_path = "/shop"  # Default for domain/subdomain access
    if access_method == "path":
        # For path-based access like /vendors/wizamart/shop
        full_prefix = (
            vendor_context.get("full_prefix", "/vendor/")
            if vendor_context
            else "/vendor/"
        )
        cookie_path = f"{full_prefix}{vendor.subdomain}/shop"

    # Set HTTP-only cookie for browser navigation
    # Cookie path matches the vendor's shop routes
    response.set_cookie(
        key="customer_token",
        value=login_result["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=login_result["token_data"]["expires_in"],  # Match JWT expiry
        path=cookie_path,  # Matches vendor's shop routes
    )

    logger.debug(
        f"Set customer_token cookie with {login_result['token_data']['expires_in']}s expiry "
        f"(path={cookie_path}, httponly=True, secure={should_use_secure_cookies()})",
        extra={
            "expires_in": login_result["token_data"]["expires_in"],
            "secure": should_use_secure_cookies(),
            "cookie_path": cookie_path,
        },
    )

    # Return full login response
    return CustomerLoginResponse(
        access_token=login_result["token_data"]["access_token"],
        token_type=login_result["token_data"]["token_type"],
        expires_in=login_result["token_data"]["expires_in"],
        user=CustomerResponse.model_validate(login_result["customer"]),
    )


@router.post("/auth/logout", response_model=LogoutResponse)
def customer_logout(request: Request, response: Response):
    """
    Customer logout for current vendor.

    Vendor is automatically determined from request context.
    Clears the customer_token cookie.
    Client should also remove token from localStorage.
    """
    # Get vendor from middleware (for logging)
    vendor = getattr(request.state, "vendor", None)

    logger.info(
        f"Customer logout for vendor {vendor.subdomain if vendor else 'unknown'}",
        extra={
            "vendor_id": vendor.id if vendor else None,
            "vendor_code": vendor.subdomain if vendor else None,
        },
    )

    # Calculate cookie path based on vendor access method (must match login)
    vendor_context = getattr(request.state, "vendor_context", None)
    access_method = (
        vendor_context.get("detection_method", "unknown")
        if vendor_context
        else "unknown"
    )

    cookie_path = "/shop"  # Default for domain/subdomain access
    if access_method == "path" and vendor:
        # For path-based access like /vendors/wizamart/shop
        full_prefix = (
            vendor_context.get("full_prefix", "/vendor/")
            if vendor_context
            else "/vendor/"
        )
        cookie_path = f"{full_prefix}{vendor.subdomain}/shop"

    # Clear the cookie (must match path used when setting)
    response.delete_cookie(
        key="customer_token",
        path=cookie_path,
    )

    logger.debug(f"Deleted customer_token cookie (path={cookie_path})")

    return LogoutResponse(message="Logged out successfully")


@router.post("/auth/forgot-password", response_model=PasswordResetRequestResponse)
def forgot_password(request: Request, email: str, db: Session = Depends(get_db)):
    """
    Request password reset for customer.

    Vendor is automatically determined from request context.
    Sends password reset email to customer if account exists.

    Request Body:
    - email: Customer email address
    """
    # Get vendor from middleware
    vendor = getattr(request.state, "vendor", None)

    if not vendor:
        raise VendorNotFoundException("context", identifier_type="subdomain")

    logger.debug(
        f"[SHOP_API] forgot_password for vendor {vendor.subdomain}",
        extra={
            "vendor_id": vendor.id,
            "vendor_code": vendor.subdomain,
            "email": email,
        },
    )

    # TODO: Implement password reset functionality
    # - Generate reset token
    # - Store token in database with expiry
    # - Send reset email to customer
    # - Return success message (don't reveal if email exists)

    logger.info(f"Password reset requested for {email} (vendor: {vendor.subdomain})")

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


@router.post("/auth/reset-password", response_model=PasswordResetResponse)
def reset_password(
    request: Request, reset_token: str, new_password: str, db: Session = Depends(get_db)
):
    """
    Reset customer password using reset token.

    Vendor is automatically determined from request context.

    Request Body:
    - reset_token: Password reset token from email
    - new_password: New password
    """
    # Get vendor from middleware
    vendor = getattr(request.state, "vendor", None)

    if not vendor:
        raise VendorNotFoundException("context", identifier_type="subdomain")

    logger.debug(
        f"[SHOP_API] reset_password for vendor {vendor.subdomain}",
        extra={
            "vendor_id": vendor.id,
            "vendor_code": vendor.subdomain,
        },
    )

    # TODO: Implement password reset
    # - Validate reset token
    # - Check token expiry
    # - Update customer password
    # - Invalidate reset token
    # - Return success

    logger.info(f"Password reset completed (vendor: {vendor.subdomain})")

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