import type { InferAttributes, Transaction } from 'sequelize';
import type { Users } from '@/db/models/users';
import type { Staff } from '@/db/models/staff';
import type { Roles } from '@/db/models/roles';
import type { Permissions } from '@/db/models/permissions';
import type { Organizations } from '@/db/models/organizations';
import type { Campuses } from '@/db/models/campuses';

/** A permission record, reduced to the fields consumers read. */
export interface PermissionLike {
  name?: string | null;
}

/**
 * The trimmed user record returned by `UsersDBApi.findProfileById`, built for
 * the `GET /me` profile in a single eager-loaded query (no per-association
 * getter round-trips). Only the columns and relations the profile DTO reads
 * are selected.
 */
export interface UserProfileRecord {
  id: string;
  email: string;
  name_prefix: string | null;
  firstName: string | null;
  lastName: string | null;
  organizationId: string | null;
  organizations: Organizations | null;
  app_role: Roles | null;
  app_role_permissions: Permissions[];
  custom_permissions: Permissions[];
  staff_user: Staff[];
  staff_campus: Campuses | null;
}

/**
 * The full user record returned by `UsersDBApi.findBy`: the user's own
 * attributes plus its eagerly-loaded associations. It is a superset of
 * {@link CurrentUser}, so it is assignable to `req.currentUser` without a cast.
 */
export type AuthenticatedUser = InferAttributes<Users> & {
  staff_user: Staff[];
  app_role: Roles | null;
  app_role_permissions: Permissions[];
  custom_permissions: Permissions[];
  organizations: Organizations | null;
};

/** Minimal shape of the authenticated user passed through the data layer. */
export interface CurrentUser {
  id: string | null;
  organizations?: { id: string | null } | null;
  organizationId?: string | null;
  app_role?: {
    globalAccess?: boolean | null;
    name?: string | null;
    /** Present on the loaded role instance attached to the request. */
    getPermissions?: () => Promise<PermissionLike[]>;
  } | null;
  /**
   * Present when the value is the full authenticated user record attached to
   * the request (e.g. `req.currentUser`), which the auth and permission layers
   * read. The data-layer methods only rely on `id`.
   */
  password?: string | null;
  custom_permissions?: PermissionLike[] | null;
  name_prefix?: string | null;
  firstName?: string | null;
  lastName?: string | null;
  email?: string | null;
  campusId?: string | null;
  campus?: { code?: string | null; name?: string | null } | null;
  staff_user?: Array<{
    campusId?: string | null;
    staff_type?: string | null;
    campus?: { code?: string | null; name?: string | null } | null;
  }> | null;
}

/** Common options accepted by db/api methods. */
export interface DbApiOptions {
  currentUser?: CurrentUser;
  transaction?: Transaction;
  countOnly?: boolean;
  ignoreDuplicates?: boolean;
  validate?: boolean;
}

/** Polymorphic file-attachment relation target. */
export interface FileRelation {
  belongsTo: string;
  belongsToColumn: string;
  belongsToId: string;
}

/** A file as received from the client or already persisted. */
export interface FileInput {
  id?: string;
  new?: boolean;
  name?: string;
  sizeInBytes?: number;
  privateUrl?: string;
  publicUrl?: string;
}
