from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session, selectinload

from app.database import get_db
from app.deps import get_current_user, is_admin, require_permission, user_role_names
from app.seed import load_catalog_products, wipe_operational_data
from app.models import (
    CompanySetting,
    Enquiry,
    EnquiryFollowup,
    EnquiryForm,
    Event,
    Expense,
    InvoiceSetting,
    Payment,
    Permission,
    QuotationSetting,
    Role,
    SystemSetting,
    User,
)
from app.schemas import (
    CompanySettingIn,
    CompanySettingOut,
    InvoiceSettingIn,
    InvoiceSettingOut,
    PermissionOut,
    QuotationSettingIn,
    QuotationSettingOut,
    RoleCreate,
    RoleOut,
    RoleUpdate,
    SystemSettingIn,
    SystemSettingOut,
    UserCreate,
    UserOut,
    UserUpdate,
)
from app.security import hash_password
from app.routers.auth import serialize_user

router = APIRouter(tags=["admin"])


@router.get("/users", response_model=list[UserOut])
def list_users(
    db: Session = Depends(get_db),
    _: User = Depends(require_permission("user.view")),
):
    users = db.query(User).options(selectinload(User.roles).selectinload(Role.permissions)).all()
    return [serialize_user(user) for user in users]


@router.get("/users/{user_id}", response_model=UserOut)
def get_user(user_id: int, db: Session = Depends(get_db), _: User = Depends(require_permission("user.view"))):
    user = db.query(User).options(selectinload(User.roles).selectinload(Role.permissions)).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return serialize_user(user)


@router.post("/users", response_model=UserOut)
def create_user(
    payload: UserCreate,
    db: Session = Depends(get_db),
    actor: User = Depends(require_permission("user.create")),
):
    if db.query(User).filter(User.email == payload.email).first():
        raise HTTPException(status_code=400, detail="Email already exists")
    roles = db.query(Role).filter(Role.id.in_(payload.role_ids)).all() if payload.role_ids else []
    if any(role.name == "Super Admin" for role in roles) and "Super Admin" not in user_role_names(actor):
        raise HTTPException(status_code=403, detail="Only Super Admin can assign Super Admin")
    user = User(
        name=payload.name,
        email=payload.email,
        phone=payload.phone,
        password_hash=hash_password(payload.password),
        is_active=payload.is_active,
        roles=roles,
    )
    db.add(user)
    db.commit()
    db.refresh(user)
    user = db.query(User).options(selectinload(User.roles).selectinload(Role.permissions)).filter(User.id == user.id).first()
    return serialize_user(user)


@router.patch("/users/{user_id}", response_model=UserOut)
def update_user(
    user_id: int,
    payload: UserUpdate,
    db: Session = Depends(get_db),
    actor: User = Depends(require_permission("user.edit")),
):
    user = db.query(User).options(selectinload(User.roles).selectinload(Role.permissions)).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    if "Super Admin" in user_role_names(user) and "Super Admin" not in user_role_names(actor):
        raise HTTPException(status_code=403, detail="Cannot modify Super Admin")
    data = payload.model_dump(exclude_unset=True)
    if "password" in data and data["password"]:
        user.password_hash = hash_password(data.pop("password"))
    else:
        data.pop("password", None)
    if "email" in data and data["email"]:
        email = str(data["email"]).strip().lower()
        conflict = db.query(User).filter(User.email == email, User.id != user.id).first()
        if conflict:
            raise HTTPException(status_code=400, detail="Email already exists")
        user.email = email
        data.pop("email")
    role_ids = data.pop("role_ids", None)
    for key, value in data.items():
        setattr(user, key, value)
    if role_ids is not None:
        roles = db.query(Role).filter(Role.id.in_(role_ids)).all()
        if any(role.name == "Super Admin" for role in roles) and "Super Admin" not in user_role_names(actor):
            raise HTTPException(status_code=403, detail="Only Super Admin can assign Super Admin")
        user.roles = roles
    db.commit()
    user = db.query(User).options(selectinload(User.roles).selectinload(Role.permissions)).filter(User.id == user.id).first()
    return serialize_user(user)


@router.delete("/users/{user_id}")
def delete_user(
    user_id: int,
    db: Session = Depends(get_db),
    actor: User = Depends(require_permission("user.delete")),
):
    if "Super Admin" not in user_role_names(actor):
        raise HTTPException(status_code=403, detail="Only Super Admin can delete accounts")
    user = db.query(User).options(selectinload(User.roles)).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    if user.id == actor.id:
        raise HTTPException(status_code=400, detail="You cannot delete your own account")
    if "Super Admin" in user_role_names(user):
        raise HTTPException(status_code=403, detail="Super Admin accounts cannot be deleted")
    db.query(Enquiry).filter(Enquiry.assigned_user_id == user.id).update({Enquiry.assigned_user_id: None})
    db.query(EnquiryFollowup).filter(EnquiryFollowup.assigned_user_id == user.id).update({EnquiryFollowup.assigned_user_id: None})
    db.query(Event).filter(Event.created_by_id == user.id).update({Event.created_by_id: None})
    db.query(Payment).filter(Payment.recorded_by_id == user.id).update({Payment.recorded_by_id: None})
    db.query(Expense).filter(Expense.added_by_id == user.id).update({Expense.added_by_id: None})
    db.query(EnquiryForm).filter(EnquiryForm.created_by_id == user.id).update({EnquiryForm.created_by_id: None})
    user.roles = []
    db.delete(user)
    db.commit()
    return {"ok": True}


@router.get("/roles", response_model=list[RoleOut])
def list_roles(db: Session = Depends(get_db), _: User = Depends(require_permission("user.view"))):
    return db.query(Role).options(selectinload(Role.permissions)).all()


@router.get("/roles/{role_id}", response_model=RoleOut)
def get_role(role_id: int, db: Session = Depends(get_db), _: User = Depends(require_permission("user.view"))):
    role = db.query(Role).options(selectinload(Role.permissions)).filter(Role.id == role_id).first()
    if role is None:
        raise HTTPException(status_code=404, detail="Role not found")
    return role


@router.post("/roles", response_model=RoleOut)
def create_role(
    payload: RoleCreate,
    db: Session = Depends(get_db),
    _: User = Depends(require_permission("user.permissions")),
):
    name = payload.name.strip()
    if not name:
        raise HTTPException(status_code=400, detail="Role name is required")
    if db.query(Role).filter(Role.name == name).first():
        raise HTTPException(status_code=400, detail="Role name already exists")
    role = Role(
        name=name,
        description=(payload.description or "").strip() or None,
        is_system=False,
        permissions=db.query(Permission).filter(Permission.key.in_(payload.permission_keys)).all(),
    )
    db.add(role)
    db.commit()
    role = db.query(Role).options(selectinload(Role.permissions)).filter(Role.id == role.id).first()
    return role


@router.get("/permissions", response_model=list[PermissionOut])
def list_permissions(db: Session = Depends(get_db), _: User = Depends(require_permission("user.view"))):
    return db.query(Permission).all()


@router.patch("/roles/{role_id}", response_model=RoleOut)
def update_role(
    role_id: int,
    payload: RoleUpdate,
    db: Session = Depends(get_db),
    _: User = Depends(require_permission("user.permissions")),
):
    role = db.query(Role).options(selectinload(Role.permissions), selectinload(Role.users)).filter(Role.id == role_id).first()
    if role is None:
        raise HTTPException(status_code=404, detail="Role not found")
    if role.name == "Super Admin":
        raise HTTPException(status_code=400, detail="Super Admin cannot be changed")

    data = payload.model_dump(exclude_unset=True)
    if "name" in data and data["name"] is not None:
        name = data["name"].strip()
        if not name:
            raise HTTPException(status_code=400, detail="Role name is required")
        if name == "Super Admin" and role.name != "Super Admin":
            raise HTTPException(status_code=400, detail="Cannot rename a role to Super Admin")
        existing = db.query(Role).filter(Role.name == name, Role.id != role.id).first()
        if existing:
            raise HTTPException(status_code=400, detail="Role name already exists")
        role.name = name
    if "description" in data:
        description = data["description"]
        role.description = (description or "").strip() or None
    if "permission_keys" in data and data["permission_keys"] is not None:
        role.permissions = db.query(Permission).filter(Permission.key.in_(data["permission_keys"])).all()

    db.commit()
    role = db.query(Role).options(selectinload(Role.permissions)).filter(Role.id == role.id).first()
    return role


@router.delete("/roles/{role_id}")
def delete_role(
    role_id: int,
    db: Session = Depends(get_db),
    _: User = Depends(require_permission("user.permissions")),
):
    role = db.query(Role).options(selectinload(Role.users)).filter(Role.id == role_id).first()
    if role is None:
        raise HTTPException(status_code=404, detail="Role not found")
    if role.name == "Super Admin":
        raise HTTPException(status_code=400, detail="Super Admin cannot be deleted")
    if role.users:
        raise HTTPException(status_code=400, detail="Role is assigned to users. Reassign them first.")
    role.permissions = []
    db.delete(role)
    db.commit()
    return {"ok": True}


@router.get("/settings/company", response_model=CompanySettingOut)
def get_company(db: Session = Depends(get_db), _: User = Depends(require_permission("settings.view"))):
    return db.query(CompanySetting).first()


@router.put("/settings/company", response_model=CompanySettingOut)
def save_company(payload: CompanySettingIn, db: Session = Depends(get_db), _: User = Depends(require_permission("settings.edit"))):
    row = db.query(CompanySetting).first()
    if row is None:
        row = CompanySetting(**payload.model_dump())
        db.add(row)
    else:
        for key, value in payload.model_dump().items():
            setattr(row, key, value)
    db.commit()
    db.refresh(row)
    return row


@router.get("/settings/invoice", response_model=InvoiceSettingOut)
def get_invoice_settings(db: Session = Depends(get_db), _: User = Depends(require_permission("settings.view"))):
    return db.query(InvoiceSetting).first()


@router.put("/settings/invoice", response_model=InvoiceSettingOut)
def save_invoice_settings(payload: InvoiceSettingIn, db: Session = Depends(get_db), actor: User = Depends(require_permission("settings.edit"))):
    if not is_admin(actor):
        raise HTTPException(status_code=403, detail="Admin only")
    row = db.query(InvoiceSetting).first()
    if row is None:
        row = InvoiceSetting(**payload.model_dump())
        db.add(row)
    else:
        for key, value in payload.model_dump().items():
            setattr(row, key, value)
    db.commit()
    db.refresh(row)
    return row


@router.get("/settings/quotation", response_model=QuotationSettingOut)
def get_quotation_settings(db: Session = Depends(get_db), _: User = Depends(require_permission("settings.view"))):
    row = db.query(QuotationSetting).first()
    if row is None:
        row = QuotationSetting(prefix="QUO", next_number=1)
        db.add(row)
        db.commit()
        db.refresh(row)
    return row


@router.put("/settings/quotation", response_model=QuotationSettingOut)
def save_quotation_settings(payload: QuotationSettingIn, db: Session = Depends(get_db), actor: User = Depends(require_permission("settings.edit"))):
    if not is_admin(actor):
        raise HTTPException(status_code=403, detail="Admin only")
    row = db.query(QuotationSetting).first()
    if row is None:
        row = QuotationSetting(**payload.model_dump())
        db.add(row)
    else:
        for key, value in payload.model_dump().items():
            setattr(row, key, value)
    db.commit()
    db.refresh(row)
    return row


@router.get("/settings/system", response_model=SystemSettingOut)
def get_system(db: Session = Depends(get_db), _: User = Depends(require_permission("settings.view"))):
    return db.query(SystemSetting).first()


@router.put("/settings/system", response_model=SystemSettingOut)
def save_system(payload: SystemSettingIn, db: Session = Depends(get_db), actor: User = Depends(require_permission("settings.edit"))):
    if "Super Admin" not in user_role_names(actor):
        raise HTTPException(status_code=403, detail="Only Super Admin can change system settings")
    row = db.query(SystemSetting).first()
    if row is None:
        row = SystemSetting(**payload.model_dump())
        db.add(row)
    else:
        for key, value in payload.model_dump().items():
            setattr(row, key, value)
    db.commit()
    db.refresh(row)
    return row


@router.post("/admin/prepare-manual-test")
def prepare_manual_test(
    db: Session = Depends(get_db),
    actor: User = Depends(get_current_user),
):
    if "Super Admin" not in user_role_names(actor):
        raise HTTPException(status_code=403, detail="Only Super Admin can reset demo data")
    wiped = wipe_operational_data(db)
    created = load_catalog_products(db)
    db.commit()
    return {
        "ok": True,
        "wiped": wiped,
        "products_loaded": created,
        "kept": ["users", "roles", "permissions", "settings"],
    }
