/**
 * Permissions & Role Utilities
 *
 * Centralized role checking and permission management for staff members.
 * Ensures DRY principle and consistent role-based access control across the application.
 */

/** Role type — mirrors the Prisma enum that was removed during Convex migration */
type Role = 'USER' | 'STAFF' | 'ADMIN' | 'SUPER_ADMIN';

/**
 * Staff roles that bypass subscription requirements.
 * Staff members are part of the admin team and don't pay for the service.
 */
export const STAFF_ROLES: readonly Role[] = ['STAFF', 'ADMIN', 'SUPER_ADMIN'] as const;

/**
 * Subscription tier constants
 */
export const SUBSCRIPTION_TIERS = {
  STARTER: 'tier1',
  PROFESSIONAL: 'tier2',
  BUSINESS: 'tier3',
  ENTERPRISE: 'tier4',
  ULTIMATE: 'tier5',
} as const;

/**
 * Staff subscription configuration
 * Used when returning mock subscription data for staff members
 */
export const STAFF_SUBSCRIPTION_CONFIG = {
  /** Unique identifier for staff access (not a real subscription ID) */
  ID: 'staff-access',
  /** Staff members get full Enterprise-level access */
  TIER_ID: SUBSCRIPTION_TIERS.ULTIMATE,
  /** Default status for staff subscriptions */
  STATUS: 'active',
  /** Default billing period */
  BILLING_PERIOD: 'monthly',
  /** Staff subscriptions don't expire (set to 1 year from now for consistency) */
  PERIOD_DURATION_MS: 365 * 24 * 60 * 60 * 1000,
} as const;

/**
 * User object with role - minimal interface for role checking
 */
interface UserWithRole {
  role?: Role | string | null;
}

/**
 * Type guard to check if a user is a staff member (STAFF, ADMIN, or SUPER_ADMIN).
 *
 * Staff members:
 * - Don't need subscriptions (they are part of the admin team)
 * - Get full feature access (Enterprise tier)
 * - Skip onboarding requirements
 * - Access both staff dashboard and user dashboard
 *
 * @param user - User object with role property, or null/undefined
 * @returns True if user is a staff member, false otherwise
 *
 * @example
 * ```typescript
 * const user = await prisma.users.findUnique({ where: { id }, select: { role: true } });
 * if (isStaffMember(user)) {
 *   // User is staff, admin, or super admin
 *   return grantFullAccess();
 * }
 * ```
 */
export function isStaffMember(user: UserWithRole | null | undefined): boolean {
  if (!user?.role) {
    return false;
  }
  return STAFF_ROLES.includes(user.role as Role);
}

/**
 * Get the staff subscription mock data.
 * Returns a consistent subscription object for staff members.
 *
 * @returns Staff subscription configuration object
 *
 * @example
 * ```typescript
 * if (isStaffMember(user)) {
 *   const subscription = getStaffSubscription();
 *   return { hasActiveSubscription: true, subscription };
 * }
 * ```
 */
export function getStaffSubscription() {
  return {
    id: STAFF_SUBSCRIPTION_CONFIG.ID,
    tierId: STAFF_SUBSCRIPTION_CONFIG.TIER_ID,
    status: STAFF_SUBSCRIPTION_CONFIG.STATUS,
    billingPeriod: STAFF_SUBSCRIPTION_CONFIG.BILLING_PERIOD,
    currentPeriodEnd: new Date(Date.now() + STAFF_SUBSCRIPTION_CONFIG.PERIOD_DURATION_MS),
    trialEndsAt: null,
    cancelAtPeriodEnd: false,
    setupFeePaid: true,
  };
}

/**
 * Get staff subscription with ISO string date (for JSON responses).
 * Use this when the response expects string dates instead of Date objects.
 *
 * @returns Staff subscription with currentPeriodEnd as ISO string
 */
export function getStaffSubscriptionForResponse() {
  const subscription = getStaffSubscription();
  return {
    ...subscription,
    currentPeriodEnd: subscription.currentPeriodEnd.toISOString(),
  };
}

/**
 * Check if a role is an admin role (ADMIN or SUPER_ADMIN).
 *
 * @param user - User object with role property
 * @returns True if user is an admin or super admin
 */
export function isAdmin(user: UserWithRole | null | undefined): boolean {
  if (!user?.role) {
    return false;
  }
  return user.role === 'ADMIN' || user.role === 'SUPER_ADMIN';
}

/**
 * Check if a role is super admin.
 *
 * @param user - User object with role property
 * @returns True if user is a super admin
 */
export function isSuperAdmin(user: UserWithRole | null | undefined): boolean {
  if (!user?.role) {
    return false;
  }
  return user.role === 'SUPER_ADMIN';
}

/**
 * Organization member subscription configuration
 * Used when returning subscription data for organization members
 */
export const ORG_MEMBER_SUBSCRIPTION_CONFIG = {
  /** Unique identifier prefix for org member access */
  ID_PREFIX: 'org-member-access',
  /** Default status for org member subscriptions */
  STATUS: 'active',
  /** Org members inherit billing period from org owner */
  DEFAULT_BILLING_PERIOD: 'monthly',
} as const;

/**
 * Result from checking organization member subscription
 */
export interface OrganizationMemberSubscriptionResult {
  hasAccess: boolean;
  subscription: {
    id: string;
    tierId: string;
    status: string;
    billingPeriod: string;
    currentPeriodEnd: Date;
    trialEndsAt: Date | null;
    cancelAtPeriodEnd: boolean;
    setupFeePaid: boolean;
  } | null;
  organization?: {
    id: string;
    name: string;
    role: string;
  };
}

/**
 * Check if a user is a member of an organization with an active subscription.
 * This allows organization members to bypass payment requirements when their
 * organization owner has already paid.
 *
 * NOTE: Only 'active' and 'trialing' subscriptions grant access.
 * 'past_due' subscriptions do NOT grant access to maintain payment compliance.
 *
 * @param userId - The user ID to check
 * @param prismaClient - Prisma client instance (must have organization_members table)
 * @returns Organization subscription data if user is member of paid org, null otherwise
 *
 * @example
 * ```typescript
 * const orgSubscription = await getOrganizationMemberSubscription(userId, prisma);
 * if (orgSubscription?.hasAccess) {
 *   // User is member of org with active subscription
 *   return { needsPayment: false, subscription: orgSubscription.subscription };
 * }
 * ```
 */
export async function getOrganizationMemberSubscription(
  userId: string,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  prismaClient: any
): Promise<OrganizationMemberSubscriptionResult | null> {
  try {
    console.log('[getOrganizationMemberSubscription] Checking membership for userId:', userId);

    // Check if organization_members table exists (graceful degradation for older schemas)
    if (!prismaClient.organization_members) {
      console.log('[getOrganizationMemberSubscription] organization_members table not available');
      return null;
    }

    // Find user's organization membership
    // NOTE: This uses a nested query which may be slow. Consider optimizing if performance becomes an issue.
    const membership = await prismaClient.organization_members.findFirst({
      where: { userId },
      include: {
        organization: {
          include: {
            owner: {
              include: {
                subscriptions: {
                  where: {
                    status: {
                      // SECURITY: Only 'active' and 'trialing' grant access
                      // Explicitly exclude 'past_due' to enforce payment compliance
                      in: ['active', 'trialing'],
                    },
                  },
                  orderBy: {
                    createdAt: 'desc',
                  },
                  take: 1,
                },
              },
            },
          },
        },
      },
    });

    console.log('[getOrganizationMemberSubscription] Membership found:', membership ? 'yes' : 'no');

    if (!membership?.organization) {
      console.log('[getOrganizationMemberSubscription] No organization membership found');
      return null;
    }

    const org = membership.organization;
    const ownerSubscription = org.owner?.subscriptions?.[0];
    console.log('[getOrganizationMemberSubscription] Org:', org.name, '| Owner has subscription:', !!ownerSubscription);

    // If org owner doesn't have active subscription, no bypass
    if (!ownerSubscription) {
      return {
        hasAccess: false,
        subscription: null,
        organization: {
          id: org.id,
          name: org.name,
          role: membership.role,
        },
      };
    }

    // SECURITY: Validate that required fields exist before granting access
    if (!ownerSubscription.currentPeriodEnd || !ownerSubscription.tierId) {
      console.error('[getOrganizationMemberSubscription] Invalid subscription data:', {
        orgId: org.id,
        subscriptionId: ownerSubscription.id,
        hasCurrentPeriodEnd: !!ownerSubscription.currentPeriodEnd,
        hasTierId: !!ownerSubscription.tierId,
      });
      return {
        hasAccess: false,
        subscription: null,
        organization: {
          id: org.id,
          name: org.name,
          role: membership.role,
        },
      };
    }

    // User is member of org with active subscription - grant access
    return {
      hasAccess: true,
      subscription: {
        id: `${ORG_MEMBER_SUBSCRIPTION_CONFIG.ID_PREFIX}-${org.id}`,
        tierId: ownerSubscription.tierId,
        status: ownerSubscription.status,
        billingPeriod: ownerSubscription.billingPeriod,
        currentPeriodEnd: ownerSubscription.currentPeriodEnd,
        trialEndsAt: ownerSubscription.trialEndsAt ?? null,
        cancelAtPeriodEnd: ownerSubscription.cancelAtPeriodEnd ?? false,
        setupFeePaid: ownerSubscription.setupFeePaid ?? true,
      },
      organization: {
        id: org.id,
        name: org.name,
        role: membership.role,
      },
    };
  } catch (error) {
    // Log error with context for debugging, but don't fail - graceful degradation
    console.error('[getOrganizationMemberSubscription] Error checking org membership:', {
      userId,
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined,
    });
    return null;
  }
}

/**
 * Get organization member subscription with ISO string date (for JSON responses).
 * Use this when the response expects string dates instead of Date objects.
 *
 * @param result - Organization member subscription result
 * @returns Subscription with currentPeriodEnd and trialEndsAt as ISO strings, or null if no subscription
 */
export function getOrgMemberSubscriptionForResponse(
  result: OrganizationMemberSubscriptionResult
) {
  if (!result.subscription) {
    return null;
  }

  return {
    ...result.subscription,
    currentPeriodEnd: result.subscription.currentPeriodEnd instanceof Date
      ? result.subscription.currentPeriodEnd.toISOString()
      : null,
    trialEndsAt: result.subscription.trialEndsAt instanceof Date
      ? result.subscription.trialEndsAt.toISOString()
      : null,
  };
}
