from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.models import Tenant, TenantAIQuota, TenantStatus, User, UserRole, UserStatus
from app.core.security import hash_password, require_platform_admin

router = APIRouter(
    prefix="/api/tenants",
    tags=["tenants"],
    dependencies=[Depends(require_platform_admin)],
)


class TenantResponse(BaseModel):
    id: int
    company_code: str
    name: str
    status: TenantStatus
    email_enabled: bool
    monthly_email_limit: int
    daily_email_limit: int
    telegram_enabled: bool
    telegram_use_platform_bot: bool
    created_at: datetime | None = None

    class Config:
        from_attributes = True


class TenantCreateRequest(BaseModel):
    company_code: str = Field(min_length=1, max_length=80)
    name: str = Field(min_length=1, max_length=200)
    admin_username: str = Field(min_length=1, max_length=100)
    admin_password: str = Field(min_length=8, max_length=255)
    status: TenantStatus = TenantStatus.active


class TenantUpdateRequest(BaseModel):
    name: str | None = Field(default=None, max_length=200)
    status: TenantStatus | None = None
    email_enabled: bool | None = None
    monthly_email_limit: int | None = Field(default=None, ge=0)
    daily_email_limit: int | None = Field(default=None, ge=0)
    telegram_enabled: bool | None = None
    telegram_use_platform_bot: bool | None = None


class TenantAdminResponse(BaseModel):
    id: int
    username: str
    display_name: str | None
    status: UserStatus

    class Config:
        from_attributes = True


class TenantAdminListResponse(BaseModel):
    items: list[TenantAdminResponse]


class TenantAdminCreateRequest(BaseModel):
    username: str = Field(min_length=1, max_length=100)
    password: str = Field(min_length=8, max_length=255)
    display_name: str | None = Field(default=None, max_length=100)


class TenantAdminUpdateRequest(BaseModel):
    username: str | None = Field(default=None, min_length=1, max_length=100)
    display_name: str | None = Field(default=None, max_length=100)
    status: UserStatus | None = None


class TenantAdminPasswordRequest(BaseModel):
    password: str = Field(min_length=8, max_length=255)


class TenantAIQuotaResponse(BaseModel):
    enabled: bool
    plan_credits: int
    override_credits: int | None
    used_credits: int
    bonus_credits: int
    five_hour_credit_limit: int
    weekly_credit_limit: int
    monthly_credit_limit: int
    unlimited: bool
    allowed_models: str | None

    class Config:
        from_attributes = True


class TenantAIQuotaUpdate(BaseModel):
    enabled: bool
    plan_credits: int = Field(ge=0)
    override_credits: int | None = Field(default=None, ge=0)
    bonus_credits: int = Field(ge=0)
    five_hour_credit_limit: int = Field(default=0, ge=0)
    weekly_credit_limit: int = Field(default=0, ge=0)
    monthly_credit_limit: int = Field(default=0, ge=0)
    unlimited: bool = False
    allowed_models: str | None = None


class TenantListResponse(BaseModel):
    items: list[TenantResponse]
    total: int


@router.get("", response_model=TenantListResponse)
def list_tenants(
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=200),
    db: Session = Depends(get_db),
):
    query = select(Tenant).order_by(Tenant.id)
    total = db.scalar(select(func.count()).select_from(Tenant)) or 0
    tenants = db.execute(
        query.offset((page - 1) * page_size).limit(page_size)
    ).scalars().all()
    return TenantListResponse(
        items=[TenantResponse.model_validate(t) for t in tenants],
        total=total,
    )


@router.post("", response_model=TenantResponse, status_code=201)
def create_tenant(data: TenantCreateRequest, db: Session = Depends(get_db)):
    code = data.company_code.strip().lower()
    existing = db.execute(
        select(Tenant).where(Tenant.company_code == code)
    ).scalar_one_or_none()
    if existing:
        raise HTTPException(status_code=400, detail="Company code already exists")

    tenant = Tenant(company_code=code, name=data.name.strip(), status=data.status)
    db.add(tenant)
    db.flush()
    db.add_all([
        TenantAIQuota(tenant_id=tenant.id),
        User(
            tenant_id=tenant.id,
            username=data.admin_username.strip(),
            password_hash=hash_password(data.admin_password),
            role=UserRole.admin,
        ),
    ])
    db.commit()
    db.refresh(tenant)
    return TenantResponse.model_validate(tenant)


def _get_tenant_admin_or_404(db: Session, tenant_id: int, user_id: int) -> User:
    admin = db.execute(
        select(User).where(User.id == user_id, User.tenant_id == tenant_id, User.role == UserRole.admin)
    ).scalar_one_or_none()
    if admin is None:
        raise HTTPException(status_code=404, detail="Tenant administrator not found")
    return admin


def _ensure_can_disable_tenant_admin(db: Session, admin: User) -> None:
    db.execute(select(Tenant).where(Tenant.id == admin.tenant_id).with_for_update()).scalar_one()
    active_admin_count = db.scalar(
        select(func.count()).select_from(User).where(
            User.tenant_id == admin.tenant_id, User.role == UserRole.admin, User.status == UserStatus.active
        )
    ) or 0
    if admin.status == UserStatus.active and active_admin_count <= 1:
        raise HTTPException(status_code=400, detail="A tenant must keep one active administrator")


@router.get("/{tenant_id}/admins", response_model=TenantAdminListResponse)
def list_tenant_admins(tenant_id: int, db: Session = Depends(get_db)):
    if not db.get(Tenant, tenant_id):
        raise HTTPException(status_code=404, detail="Tenant not found")
    admins = db.execute(
        select(User).where(User.tenant_id == tenant_id, User.role == UserRole.admin).order_by(User.id)
    ).scalars().all()
    return TenantAdminListResponse(items=[TenantAdminResponse.model_validate(admin) for admin in admins])


@router.post("/{tenant_id}/admins", response_model=TenantAdminResponse, status_code=201)
def create_tenant_admin(tenant_id: int, data: TenantAdminCreateRequest, db: Session = Depends(get_db)):
    if not db.get(Tenant, tenant_id):
        raise HTTPException(status_code=404, detail="Tenant not found")
    username = data.username.strip()
    if not username:
        raise HTTPException(status_code=422, detail="Username cannot be blank")
    if db.execute(select(User).where(User.tenant_id == tenant_id, User.username == username)).scalar_one_or_none():
        raise HTTPException(status_code=400, detail="Username already exists")
    admin = User(
        tenant_id=tenant_id,
        username=username,
        password_hash=hash_password(data.password),
        display_name=data.display_name.strip() if data.display_name else None,
        role=UserRole.admin,
    )
    db.add(admin)
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(status_code=400, detail="Username already exists")
    db.refresh(admin)
    return TenantAdminResponse.model_validate(admin)


@router.put("/{tenant_id}/admins/{user_id}", response_model=TenantAdminResponse)
def update_tenant_admin(tenant_id: int, user_id: int, data: TenantAdminUpdateRequest, db: Session = Depends(get_db)):
    admin = _get_tenant_admin_or_404(db, tenant_id, user_id)
    if data.username is not None:
        username = data.username.strip()
        if not username:
            raise HTTPException(status_code=422, detail="Username cannot be blank")
        duplicate = db.execute(
            select(User).where(User.tenant_id == tenant_id, User.username == username, User.id != user_id)
        ).scalar_one_or_none()
        if duplicate:
            raise HTTPException(status_code=400, detail="Username already exists")
        admin.username = username
    if data.display_name is not None:
        admin.display_name = data.display_name.strip() or None
    if data.status is not None:
        if data.status == UserStatus.disabled:
            _ensure_can_disable_tenant_admin(db, admin)
        admin.status = data.status
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(status_code=400, detail="Username already exists")
    db.refresh(admin)
    return TenantAdminResponse.model_validate(admin)


@router.post("/{tenant_id}/admins/{user_id}/reset-password")
def reset_tenant_admin_password(tenant_id: int, user_id: int, data: TenantAdminPasswordRequest, db: Session = Depends(get_db)):
    admin = _get_tenant_admin_or_404(db, tenant_id, user_id)
    admin.password_hash = hash_password(data.password)
    db.commit()
    return {"message": "Password reset successfully"}


@router.delete("/{tenant_id}/admins/{user_id}", response_model=TenantAdminResponse)
def disable_tenant_admin(tenant_id: int, user_id: int, db: Session = Depends(get_db)):
    admin = _get_tenant_admin_or_404(db, tenant_id, user_id)
    _ensure_can_disable_tenant_admin(db, admin)
    admin.status = UserStatus.disabled
    db.commit()
    db.refresh(admin)
    return TenantAdminResponse.model_validate(admin)


@router.get("/{tenant_id}", response_model=TenantResponse)
def get_tenant(tenant_id: int, db: Session = Depends(get_db)):
    tenant = db.get(Tenant, tenant_id)
    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")
    return TenantResponse.model_validate(tenant)


@router.put("/{tenant_id}", response_model=TenantResponse)
def update_tenant(tenant_id: int, data: TenantUpdateRequest, db: Session = Depends(get_db)):
    tenant = db.get(Tenant, tenant_id)
    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    if data.name is not None:
        tenant.name = data.name.strip()
    if data.status is not None:
        tenant.status = data.status
    if data.email_enabled is not None:
        tenant.email_enabled = data.email_enabled
    if data.monthly_email_limit is not None:
        tenant.monthly_email_limit = data.monthly_email_limit
    if data.daily_email_limit is not None:
        tenant.daily_email_limit = data.daily_email_limit
    if data.telegram_enabled is not None:
        tenant.telegram_enabled = data.telegram_enabled
    if data.telegram_use_platform_bot is not None:
        tenant.telegram_use_platform_bot = data.telegram_use_platform_bot
    db.commit()
    db.refresh(tenant)
    return TenantResponse.model_validate(tenant)


@router.get("/{tenant_id}/ai-quota", response_model=TenantAIQuotaResponse)
def get_tenant_ai_quota(tenant_id: int, db: Session = Depends(get_db)):
    if not db.get(Tenant, tenant_id):
        raise HTTPException(status_code=404, detail="Tenant not found")
    quota = db.execute(select(TenantAIQuota).where(TenantAIQuota.tenant_id == tenant_id)).scalar_one_or_none()
    if quota is None:
        quota = TenantAIQuota(tenant_id=tenant_id)
        db.add(quota)
        db.commit()
        db.refresh(quota)
    return TenantAIQuotaResponse.model_validate(quota)


@router.put("/{tenant_id}/ai-quota", response_model=TenantAIQuotaResponse)
def update_tenant_ai_quota(tenant_id: int, data: TenantAIQuotaUpdate, db: Session = Depends(get_db)):
    quota = get_tenant_ai_quota(tenant_id, db)
    row = db.execute(select(TenantAIQuota).where(TenantAIQuota.tenant_id == tenant_id)).scalar_one()
    for field, value in data.model_dump().items():
        setattr(row, field, value)
    db.commit()
    db.refresh(row)
    return TenantAIQuotaResponse.model_validate(row)
