from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from datetime import datetime, timedelta
from typing import List, Optional

from ...db import get_session
from ...auth_security import AdminContext, require_admin, require_account, require_user
from ...models import User, UserRole
from ... import settings as saas_settings

router = APIRouter(prefix="/api/v2/admin", tags=["admin"])


class RoleBody(BaseModel):
    role: str


class AuditLog(BaseModel):
    id: str
    action: str
    user_email: str
    target_email: Optional[str]
    timestamp: datetime
    details: Optional[dict]


class PendingInvite(BaseModel):
    id: str
    email: str
    role: str
    invited_by: str
    invited_at: datetime
    expires_at: datetime


def _normalize_role(role: str) -> UserRole:
    r = (role or "").strip().lower()
    if r in ("admin", "owner"):
        return UserRole.ADMIN
    if r in ("manager", "steward"):
        return UserRole.STEWARD
    if r in ("member", "viewer"):
        return UserRole.VIEWER
    raise HTTPException(400, "Bad role")


def _serialize_role(role_value: object) -> str:
    """Return canonical string for a stored role value."""
    resolved = UserRole.from_raw(role_value) or UserRole.VIEWER
    if resolved == UserRole.OWNER:
        return UserRole.ADMIN.value
    return resolved.value


@router.get("/users")
async def users_list(
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
    current_user_id: str = Depends(require_user),
):
    rows = (
        (await db.execute(select(User).where(User.account_id == account_id)))
        .scalars()
        .all()
    )
    return [
        {
            "user_id": u.id,
            "email": u.email,
            "role": _serialize_role(getattr(u, "role", None)),
            "created_at": (
                u.created_at.isoformat() if getattr(u, "created_at", None) else None
            ),
        }
        for u in rows
    ]


@router.post("/users/{user_id}/role")
async def set_role(
    user_id: str,
    body: RoleBody,
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
    current_user_id: str = Depends(require_user),
):
    # Ensure caller is admin
    me = (
        await db.execute(select(User).where(User.id == current_user_id))
    ).scalar_one_or_none()
    is_admin_email = bool(me and me.email and me.email in saas_settings.ADMIN_EMAILS)
    if not me or (not UserRole.is_admin_value(me.role) and not is_admin_email):
        raise HTTPException(403, "Insufficient role")

    # Only manage users within same account
    target = (
        await db.execute(
            select(User).where(User.id == user_id, User.account_id == account_id)
        )
    ).scalar_one_or_none()
    if not target:
        raise HTTPException(404, "User not found in this account")

    # Prevent demoting self below admin
    new_role = _normalize_role(body.role)
    if user_id == current_user_id and new_role not in UserRole.admin_roles():
        raise HTTPException(400, "Refuse to demote self")

    target.role = new_role
    db.add(target)
    await db.commit()

    # Log the action (if audit log table exists)
    # TODO: Create actual audit log entry if you have an AuditLog table

    return {"ok": True}


@router.delete("/users/{user_id}")
async def remove_user(
    user_id: str,
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
    current_user_id: str = Depends(require_user),
):
    """Remove a user from the organization."""
    # Ensure caller is admin
    me = (
        await db.execute(select(User).where(User.id == current_user_id))
    ).scalar_one_or_none()
    is_admin_email = bool(me and me.email and me.email in saas_settings.ADMIN_EMAILS)
    if not me or (not UserRole.is_admin_value(me.role) and not is_admin_email):
        raise HTTPException(403, "Insufficient role")

    # Prevent self-removal
    if user_id == current_user_id:
        raise HTTPException(400, "Cannot remove yourself")

    # Only remove users within same account
    target = (
        await db.execute(
            select(User).where(User.id == user_id, User.account_id == account_id)
        )
    ).scalar_one_or_none()
    if not target:
        raise HTTPException(404, "User not found in this account")

    # Remove the user
    await db.delete(target)
    await db.commit()

    return {"ok": True, "message": f"User {target.email} removed"}


@router.get("/audit-logs")
async def get_audit_logs(
    limit: int = 10,
    admin_ctx: AdminContext = Depends(require_admin),
) -> List[dict]:
    """Get recent admin actions audit log."""
    actor_email = admin_ctx.email or "admin@localhost"

    # TODO: Implement actual audit log retrieval if you have an AuditLog table
    # For now, return mock data for frontend testing
    mock_logs = [
        {
            "id": "log1",
            "action": "role_change",
            "user_email": actor_email,
            "target_email": "user@example.com",
            "timestamp": (datetime.utcnow() - timedelta(hours=2)).isoformat(),
            "details": {"old_role": "member", "new_role": "admin"},
        },
        {
            "id": "log2",
            "action": "user_invited",
            "user_email": actor_email,
            "target_email": "newuser@example.com",
            "timestamp": (datetime.utcnow() - timedelta(hours=5)).isoformat(),
            "details": {"role": "member"},
        },
        {
            "id": "log3",
            "action": "user_removed",
            "user_email": actor_email,
            "target_email": "removed@example.com",
            "timestamp": (datetime.utcnow() - timedelta(days=1)).isoformat(),
            "details": {},
        },
    ]

    return mock_logs[:limit]


@router.get("/invites")
async def get_pending_invites(
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
    current_user_id: str = Depends(require_user),
) -> List[dict]:
    """Get all pending invitations."""
    # Ensure caller is admin
    me = (
        await db.execute(select(User).where(User.id == current_user_id))
    ).scalar_one_or_none()
    is_admin_email = bool(me and me.email and me.email in saas_settings.ADMIN_EMAILS)
    if not me or (not UserRole.is_admin_value(me.role) and not is_admin_email):
        raise HTTPException(403, "Insufficient role")

    # TODO: Implement actual invite retrieval if you have an Invites table
    # For now, return mock data for frontend testing
    mock_invites = [
        {
            "id": "inv1",
            "email": "pending1@example.com",
            "role": "member",
            "invited_by": me.email,
            "invited_at": (datetime.utcnow() - timedelta(days=2)).isoformat(),
            "expires_at": (datetime.utcnow() + timedelta(days=5)).isoformat(),
        },
        {
            "id": "inv2",
            "email": "pending2@example.com",
            "role": "manager",
            "invited_by": me.email,
            "invited_at": (datetime.utcnow() - timedelta(days=1)).isoformat(),
            "expires_at": (datetime.utcnow() + timedelta(days=6)).isoformat(),
        },
    ]

    return mock_invites


@router.post("/invites/{invite_id}/resend")
async def resend_invite(
    invite_id: str,
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
    current_user_id: str = Depends(require_user),
):
    """Resend an invitation email."""
    # Ensure caller is admin
    me = (
        await db.execute(select(User).where(User.id == current_user_id))
    ).scalar_one_or_none()
    is_admin_email = bool(me and me.email and me.email in saas_settings.ADMIN_EMAILS)
    if not me or (not UserRole.is_admin_value(me.role) and not is_admin_email):
        raise HTTPException(403, "Insufficient role")

    # TODO: Implement actual invite resending logic
    # This would typically:
    # 1. Look up the invite by ID
    # 2. Generate a new token or extend expiration
    # 3. Send the email

    return {"ok": True, "message": f"Invitation {invite_id} resent"}


@router.delete("/invites/{invite_id}")
async def cancel_invite(
    invite_id: str,
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
    current_user_id: str = Depends(require_user),
):
    """Cancel a pending invitation."""
    # Ensure caller is admin
    me = (
        await db.execute(select(User).where(User.id == current_user_id))
    ).scalar_one_or_none()
    is_admin_email = bool(me and me.email and me.email in saas_settings.ADMIN_EMAILS)
    if not me or (not UserRole.is_admin_value(me.role) and not is_admin_email):
        raise HTTPException(403, "Insufficient role")

    # TODO: Implement actual invite cancellation
    # This would typically:
    # 1. Look up the invite by ID
    # 2. Delete it from the database

    return {"ok": True, "message": f"Invitation {invite_id} cancelled"}
