# app/modules/auth/routes.py
import uuid
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, HTTPException, Depends
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, or_, func
from app.core.database import get_db
from app.core.security import create_access_token, verify_password, require_permission, get_current_user, get_effective_permissions
from app.models.core_models import LoginUsers
from app.models.access_models import RoleMaster
from app.models.rm_models import VendorMaster
from app.models.vendor_portal import VendorPortalLog
from app.models.sales_models import CustomerMaster
from app.models.dealer_models import DealerPortalLog
from app.modules.access_control.schemas import MyPermissionOut
from pydantic import BaseModel

router = APIRouter()

class LoginRequest(BaseModel):
    username: str
    password: str
 
@router.post('/login')
async def login(data: LoginRequest, db: AsyncSession = Depends(get_db)):
    query = (data.username or '').strip().lower()
    result = await db.execute(
        select(LoginUsers).where(
            or_(
                func.lower(LoginUsers.employee_code) == query,
                func.lower(LoginUsers.email) == query
            )
        )
    )
    user = result.scalar_one_or_none()
    if not user or not verify_password(data.password, user.password_hash):
        raise HTTPException(status_code=401, detail="Incorrect username or password")
    if not user.is_active:
        raise HTTPException(status_code=401, detail="User account is inactive")
    access_token = create_access_token(data={"sub": user.employee_code, "user_id": str(user.user_id), "role": user.role_code, "name": user.name})
    return {"access_token": access_token, "token_type": "bearer", "user": {"name": user.name, "role": user.role_code}}
 
@router.post('/token')
async def login_oauth(form_data: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)):
    query = (form_data.username or '').strip().lower()
    result = await db.execute(
        select(LoginUsers).where(
            or_(
                func.lower(LoginUsers.employee_code) == query,
                func.lower(LoginUsers.email) == query
            )
        )
    )
    user = result.scalar_one_or_none()
    if not user or not verify_password(form_data.password, user.password_hash):
        raise HTTPException(status_code=401, detail="Incorrect username or password")
    if not user.is_active:
        raise HTTPException(status_code=401, detail="User account is inactive")
    access_token = create_access_token(data={"sub": user.employee_code, "user_id": str(user.user_id), "role": user.role_code, "name": user.name})
    return {"access_token": access_token, "token_type": "bearer", "user": {"name": user.name, "role": user.role_code}}

@router.get('/me')
async def get_me(user: LoginUsers = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
    """
    The logged-in user's own profile — who they are (name, employee code, email) and which
    role they're on (by role_id/role_code, resolved to its display name). Nothing here is
    derived from a username; it's the same login_users row every other endpoint reads.
    """
    role = (await db.execute(
        select(RoleMaster).where(RoleMaster.role_code == user.role_code)
    )).scalar_one_or_none()
    return {
        "user_id": str(user.user_id),
        "employee_code": user.employee_code,
        "name": user.name,
        "email": user.email,
        "mobile": user.mobile,
        "role_code": user.role_code,
        "role_name": role.role_name if role else user.role_code,
    }


@router.get('/me/permissions', response_model=list[MyPermissionOut])
async def get_my_permissions(user: LoginUsers = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
    """
    What the logged-in user is actually permitted to do, module by module — the single
    source of truth the frontend uses to decide what to show, instead of hardcoding
    role-to-page maps that can drift out of sync with the real permission matrix.
    """
    return await get_effective_permissions(db, user)


@router.get('/portal-vendors')
async def list_portal_vendors(
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    """
    Vendors an ADMIN can 'view as' in the Vendor Portal, for oversight/testing.
    Only vendors with the portal actually enabled show up here.
    """
    result = await db.execute(
        select(VendorMaster.vendor_id, VendorMaster.name)
        .where(VendorMaster.portal_enabled == True, VendorMaster.is_active == True)
        .order_by(VendorMaster.name)
    )
    return [{"vendor_id": str(row.vendor_id), "name": row.name} for row in result.all()]


@router.post('/view-as-vendor/{vendor_id}')
async def view_as_vendor(
    vendor_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    admin: LoginUsers = Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    """
    Mints a real vendor-portal session/token for the chosen vendor, so an ADMIN can
    view the Vendor Portal exactly as that vendor would see it, without their password.
    """
    vendor = await db.get(VendorMaster, vendor_id)
    if not vendor or not vendor.portal_enabled:
        raise HTTPException(status_code=404, detail="Vendor not found or portal not enabled")

    log_entry = VendorPortalLog(
        vendor_id=vendor.vendor_id,
        session_token=str(uuid.uuid4().hex),
        ip_address="admin-impersonation",
        user_agent=f"admin:{admin.employee_code}",
        expires_at=datetime.now(timezone.utc) + timedelta(days=30),
    )
    db.add(log_entry)
    await db.commit()
    await db.refresh(log_entry)

    access_token = create_access_token({
        "sub": str(vendor.vendor_id),
        "sub_type": "vendor",
        "session_id": str(log_entry.log_id),
    })
    return {"access_token": access_token, "vendor_id": str(vendor.vendor_id), "vendor_name": vendor.name}


@router.get('/portal-dealers')
async def list_portal_dealers(
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    """
    Dealers an ADMIN can 'view as' in the Dealer Portal, for oversight/testing.
    Only dealers with the portal actually enabled show up here.
    """
    result = await db.execute(
        select(CustomerMaster.customer_id, CustomerMaster.name)
        .where(CustomerMaster.portal_enabled == True, CustomerMaster.is_active == True)
        .order_by(CustomerMaster.name)
    )
    return [{"dealer_id": row.customer_id, "name": row.name} for row in result.all()]


@router.post('/view-as-dealer/{dealer_id}')
async def view_as_dealer(
    dealer_id: str,
    db: AsyncSession = Depends(get_db),
    admin: LoginUsers = Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    """
    Mints a real dealer-portal session/token for the chosen dealer, so an ADMIN can
    view the Dealer Portal exactly as that dealer would see it, without their password.
    """
    dealer = await db.get(CustomerMaster, dealer_id)
    if not dealer or not dealer.portal_enabled:
        raise HTTPException(status_code=404, detail="Dealer not found or portal not enabled")

    log_entry = DealerPortalLog(
        dealer_id=dealer.customer_id,
        session_token=str(uuid.uuid4().hex),
        ip_address="admin-impersonation",
        user_agent=f"admin:{admin.employee_code}",
        expires_at=datetime.now(timezone.utc) + timedelta(days=30),
    )
    db.add(log_entry)
    await db.commit()
    await db.refresh(log_entry)

    access_token = create_access_token({
        "sub": dealer.customer_id,
        "sub_type": "dealer",
        "session_id": str(log_entry.log_id),
    })
    return {"access_token": access_token, "dealer_id": dealer.customer_id, "dealer_name": dealer.name}
