/**
 * Organization Admin Middleware
 *
 * Provides `requireOrgAdmin` middleware and `orgScopeFilter` helper for
 * org-level admin routes. Only users with `isOrgAdmin: true` (granted by
 * super-admin) or `role === 'super-admin'` can access org-admin routes.
 *
 * Org-admin routes are scoped to the user's `activeCompanyId`, ensuring
 * complete data isolation between organisations.
 */

import { Request, Response, NextFunction } from 'express';

// ---------------------------------------------------------------------------
// Org context attached to req by requireOrgAdmin
// ---------------------------------------------------------------------------

export interface OrgContext {
  companyId: string;
  isAdmin: boolean;
  isSuperAdmin: boolean;
  /**
   * The full set of companies the acting admin manages. For an org-admin this
   * is their `User.companyIds`; for a super-admin it is empty (super-admin
   * bypasses subset checks and the scope filters short-circuit to `{}`).
   * Roles/users created by the admin apply across this whole set, so the
   * admin never needs to re-create them per company.
   */
  managedCompanyIds: string[];
}

declare global {
  namespace Express {
    interface Request {
      orgContext?: OrgContext;
    }
  }
}

// ---------------------------------------------------------------------------
// requireOrgAdmin middleware
// ---------------------------------------------------------------------------

/**
 * Middleware that requires the authenticated user to be an org admin or super-admin.
 *
 * - Super-admin: always passes, can see all data (no scope filter)
 * - Org admin (isOrgAdmin: true): passes, scoped to their activeCompanyId
 * - Other users: 403 Forbidden
 */
export function requireOrgAdmin(req: Request, res: Response, next: NextFunction): void {
  if (!req.user) {
    res.status(401).json({ error: 'Authentication required' });
    return;
  }

  const user = req.user as any;

  // Super-admin always has access
  if (user.role === 'super-admin') {
    req.orgContext = {
      companyId: user.activeCompanyId || (user.companyIds?.[0] || ''),
      isAdmin: true,
      isSuperAdmin: true,
      managedCompanyIds: [],
    };
    next();
    return;
  }

  // Org admin must have isOrgAdmin flag AND an active company
  if (!user.isOrgAdmin) {
    res.status(403).json({ error: 'Organization admin access required. Contact your platform administrator to grant access.' });
    return;
  }

  const companyId = user.activeCompanyId || (user.companyIds?.[0] || '');
  if (!companyId) {
    res.status(400).json({ error: 'No active company assigned. Please switch to a company first.' });
    return;
  }

  req.orgContext = {
    companyId,
    isAdmin: true,
    isSuperAdmin: false,
    // The admin's roles/users apply across every company they are linked to.
    managedCompanyIds: Array.isArray(user.companyIds) ? user.companyIds : [companyId],
  };

  next();
}

// ---------------------------------------------------------------------------
// orgScopeFilter helper
// ---------------------------------------------------------------------------

/**
 * Returns a MongoDB filter object for scoping role queries to the org admin.
 *
 * - For super-admin: returns {} (no filter — can see everything)
 * - For org admin: global roles + the active company's roles + the admin's
 *   custom roles that were created while active in another managed company.
 *   Other managed companies' seeded org-default copies are deliberately
 *   excluded (via `isOrgDefault: {$ne: true}`) so default roles like
 *   Manager/Editor/Viewer still appear as a single row, exactly as before.
 */
export function orgScopeFilter(orgContext: OrgContext): Record<string, any> {
  if (orgContext.isSuperAdmin) {
    return {};
  }
  const managed = orgContext.managedCompanyIds?.length ? orgContext.managedCompanyIds : [orgContext.companyId];
  const otherManaged = managed.filter((c) => c !== orgContext.companyId);
  const orClauses: Record<string, any>[] = [
    { scope: 'global' },
    { scope: orgContext.companyId },
  ];
  if (otherManaged.length > 0) {
    // Custom (non-org-default) roles scoped to other managed companies — these
    // are the admin's own roles created from another company, now visible everywhere.
    orClauses.push({ scope: { $in: otherManaged }, isOrgDefault: { $ne: true } });
  }
  return { $or: orClauses };
}

/**
 * Returns a MongoDB filter for scoping user queries to the org admin's companies.
 * Matches users who belong to any of the admin's managed companies, and excludes
 * super-admin users.
 */
export function orgUserFilter(orgContext: OrgContext): Record<string, any> {
  if (orgContext.isSuperAdmin) {
    return {};
  }
  const managed = orgContext.managedCompanyIds?.length ? orgContext.managedCompanyIds : [orgContext.companyId];
  return {
    companyIds: { $in: managed },
    role: { $ne: 'super-admin' },
  };
}