"""
Organization management (SaaS multi-tenant replacement for /clients).

Access rules:
  - Organization CRUD     → Admin only (platform)
  - List members          → Admin or org MANAGER+
  - Add / update member   → Admin or org ADMIN+
  - Remove member         → Admin or org ADMIN+ (cannot remove OWNER)

During the shim period this router delegates reads/writes to the same
underlying MongoDB collections used by routes_clients.py so both
endpoints coexist without data duplication.
"""

from datetime import UTC, datetime

from fastapi import APIRouter, Depends, HTTPException, Request
from motor.motor_asyncio import AsyncIOMotorDatabase
from pydantic import BaseModel

from ...core.authz import bump_user_membership_cache
from ...core.database import get_database
from ...core.dependencies import get_current_user, require_roles
from ...models.audit_log import AuditAction
from ...models.membership import MemberDetail, MembershipCreate, MembershipUpdate
from ...models.organization import (
    Organization,
    OrganizationCreate,
    OrganizationUpdate,
    OrgRole,
)
from ...models.user import User, UserRole
from ...services.audit_logger import audit_logger
from ...services.membership_service import (
    get_membership,
    get_memberships_for_user,
    list_org_members,
    remove_membership,
    upsert_membership,
)

router = APIRouter(prefix="/organizations", tags=["organizations"])

ADMIN_ROLES = [UserRole.ADMIN]


def _now() -> datetime:
    return datetime.now(UTC)


# ---------------------------------------------------------------------------
# Helpers — read from "clients" collection (shim: same data, new name)
# ---------------------------------------------------------------------------

async def _get_org_or_404(org_id: str, db: AsyncIOMotorDatabase) -> dict:
    # Try organizations collection first, fall back to clients (shim)
    doc = await db.organizations.find_one({"_id": org_id})
    if not doc:
        doc = await db.clients.find_one({"_id": org_id})
    if not doc:
        raise HTTPException(status_code=404, detail="Organization not found")
    return doc


def _org_from_doc(doc: dict) -> Organization:
    return Organization(
        id=str(doc["_id"]),
        name=doc["name"],
        slug=doc["slug"],
        is_active=doc.get("is_active", True),
        plan=doc.get("plan", "standard"),
        created_at=doc.get("created_at"),
        updated_at=doc.get("updated_at"),
    )


async def _assert_org_access(
    org_id: str,
    min_role: OrgRole,
    current_user: User,
    db: AsyncIOMotorDatabase,
) -> None:
    if current_user.role == UserRole.ADMIN:
        return
    membership = await get_membership(current_user.id, org_id, db)
    if not membership or membership.role_in_org < min_role:
        raise HTTPException(status_code=403, detail="Insufficient organization access")


# ---------------------------------------------------------------------------
# Organization CRUD
# ---------------------------------------------------------------------------

@router.get("", response_model=list[Organization])
async def list_organizations(
    current_user: User = Depends(require_roles(UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    docs = []
    async for doc in db.clients.find({"is_active": True}).sort("name", 1):
        docs.append(_org_from_doc(doc))
    return docs


@router.get("/{org_id}", response_model=Organization)
async def get_organization(
    org_id: str,
    current_user: User = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    await _assert_org_access(org_id, OrgRole.VIEWER, current_user, db)
    doc = await _get_org_or_404(org_id, db)
    return _org_from_doc(doc)


class _OrgCreate(BaseModel):
    name: str
    slug: str


@router.post("", response_model=Organization, status_code=201)
async def create_organization(
    body: OrganizationCreate,
    request: Request,
    current_user: User = Depends(require_roles(UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    existing = await db.clients.find_one({"slug": body.slug})
    if existing:
        raise HTTPException(status_code=409, detail="Slug already in use")

    now = _now()
    doc = {
        "_id": body.slug,
        "name": body.name,
        "slug": body.slug,
        "is_active": True,
        "plan": "standard",
        "created_at": now,
        "updated_at": now,
    }
    await db.clients.insert_one(doc)
    org = _org_from_doc(doc)
    await audit_logger.log_action(
        action=AuditAction.ORG_CREATE,
        description=f"Organization '{org.name}' created",
        user=current_user,
        request=request,
        resource_type="organization",
        resource_id=str(org.id),
        resource_name=org.name,
        details={"slug": org.slug},
    )
    return org


@router.patch("/{org_id}", response_model=Organization)
async def update_organization(
    org_id: str,
    body: OrganizationUpdate,
    request: Request,
    current_user: User = Depends(require_roles(UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    doc = await _get_org_or_404(org_id, db)
    updates: dict = {"updated_at": _now()}
    if body.name is not None:
        updates["name"] = body.name
    if body.slug is not None:
        updates["slug"] = body.slug
    if body.is_active is not None:
        updates["is_active"] = body.is_active
    if body.plan is not None:
        updates["plan"] = body.plan

    await db.clients.update_one({"_id": org_id}, {"$set": updates})
    updated = {**doc, **updates}
    org = _org_from_doc(updated)
    await audit_logger.log_action(
        action=AuditAction.ORG_UPDATE,
        description=f"Organization '{org.name}' updated",
        user=current_user,
        request=request,
        resource_type="organization",
        resource_id=str(org.id),
        resource_name=org.name,
        details={k: v for k, v in updates.items() if k != "updated_at"},
    )
    return org


# ---------------------------------------------------------------------------
# Member management
# ---------------------------------------------------------------------------

@router.get("/{org_id}/members", response_model=list[MemberDetail])
async def list_members(
    org_id: str,
    current_user: User = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    await _assert_org_access(org_id, OrgRole.MANAGER, current_user, db)
    await _get_org_or_404(org_id, db)
    return await list_org_members(org_id, db)


@router.post("/{org_id}/members", response_model=MemberDetail, status_code=201)
async def add_member(
    org_id: str,
    body: MembershipCreate,
    request: Request,
    current_user: User = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    await _assert_org_access(org_id, OrgRole.ADMIN, current_user, db)
    await _get_org_or_404(org_id, db)

    user_doc = await db.users.find_one({"_id": body.user_id})
    if not user_doc:
        raise HTTPException(status_code=404, detail="User not found")

    await upsert_membership(body.user_id, org_id, body.role_in_org, current_user.id, db)
    await bump_user_membership_cache(body.user_id)
    members = await list_org_members(org_id, db)
    for m in members:
        if m.user_id == body.user_id:
            await audit_logger.log_action(
                action=AuditAction.ORG_MEMBER_ADD,
                description=f"Member '{body.user_id}' added to organization '{org_id}' with role '{body.role_in_org}'",
                user=current_user,
                request=request,
                resource_type="organization",
                resource_id=org_id,
                details={"user_id": body.user_id, "role": body.role_in_org},
            )
            return m
    raise HTTPException(status_code=500, detail="Membership created but could not be retrieved")


@router.patch("/{org_id}/members/{user_id}", response_model=MemberDetail)
async def update_member(
    org_id: str,
    user_id: str,
    body: MembershipUpdate,
    request: Request,
    current_user: User = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    await _assert_org_access(org_id, OrgRole.ADMIN, current_user, db)

    existing = await get_membership(user_id, org_id, db)
    if not existing:
        raise HTTPException(status_code=404, detail="Membership not found")
    if existing.role_in_org == OrgRole.OWNER and body.role_in_org != OrgRole.OWNER:
        raise HTTPException(status_code=403, detail="Cannot change OWNER role via this endpoint")

    await upsert_membership(user_id, org_id, body.role_in_org, current_user.id, db)
    await bump_user_membership_cache(user_id)
    members = await list_org_members(org_id, db)
    for m in members:
        if m.user_id == user_id:
            await audit_logger.log_action(
                action=AuditAction.ORG_MEMBER_UPDATE,
                description=f"Member '{user_id}' role updated in organization '{org_id}' to '{body.role_in_org}'",
                user=current_user,
                request=request,
                resource_type="organization",
                resource_id=org_id,
                details={"user_id": user_id, "role": body.role_in_org},
            )
            return m
    raise HTTPException(status_code=500, detail="Could not retrieve updated membership")


@router.delete("/{org_id}/members/{user_id}", status_code=204)
async def remove_member(
    org_id: str,
    user_id: str,
    request: Request,
    current_user: User = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    await _assert_org_access(org_id, OrgRole.ADMIN, current_user, db)

    existing = await get_membership(user_id, org_id, db)
    if not existing:
        raise HTTPException(status_code=404, detail="Membership not found")
    if existing.role_in_org == OrgRole.OWNER:
        raise HTTPException(status_code=403, detail="Cannot remove an OWNER from the organization")

    await remove_membership(user_id, org_id, db)
    await bump_user_membership_cache(user_id)
    await audit_logger.log_action(
        action=AuditAction.ORG_MEMBER_REMOVE,
        description=f"Member '{user_id}' removed from organization '{org_id}'",
        user=current_user,
        request=request,
        resource_type="organization",
        resource_id=org_id,
        details={"user_id": user_id, "role": existing.role_in_org},
    )


# ---------------------------------------------------------------------------
# Current user's memberships
# ---------------------------------------------------------------------------

@router.get("/me/memberships", response_model=list[dict])
async def get_my_memberships(
    current_user: User = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    memberships = await get_memberships_for_user(current_user.id, db)
    result = []
    for m in memberships:
        doc = await _get_org_or_404(m.organization_id, db)
        org = _org_from_doc(doc)
        result.append({
            "organization_id": m.organization_id,
            "organization_name": org.name,
            "organization_slug": org.slug,
            "role_in_org": m.role_in_org,
            "membership_id": m.id,
        })
    return result
