import type { InferAttributes, Transaction } from 'sequelize';
import type { Users } from '@/db/models/users';
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';
import type { Schools } from '@/db/models/schools';
import type { Classes } from '@/db/models/classes';
import type { File } from '@/db/models/file';

/** 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;
  phoneNumber: string | null;
  organizationId: string | null;
  schoolId: string | null;
  campusId: string | null;
  classId: string | null;
  organizations: Organizations | null;
  school: Schools | null;
  campus: Campuses | null;
  class: Classes | null;
  app_role: Roles | null;
  app_role_permissions: Permissions[];
  custom_permissions: Permissions[];
  custom_permissions_filter: Permissions[];
  avatar: File[];
}

/**
 * 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> & {
  app_role: Roles | null;
  app_role_permissions: Permissions[];
  custom_permissions: Permissions[];
  custom_permissions_filter: Permissions[];
  organizations: Organizations | null;
  /** Drill-down override resolved by the active-scope middleware. */
  activeScope?: CurrentUser['activeScope'];
};

/** 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;
    /** Authorization scope of the role (`roles.scope`): organization/school/campus/… */
    scope?: string | null;
    /** Present on the loaded role instance attached to the request. */
    getPermissions?: () => Promise<PermissionLike[]>;
    /** Present when role permissions are eager-loaded with the request user. */
    permissions?: PermissionLike[] | null;
  } | 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;
  custom_permissions_filter?: PermissionLike[] | null;
  name_prefix?: string | null;
  firstName?: string | null;
  lastName?: string | null;
  phoneNumber?: string | null;
  email?: string | null;
  campusId?: string | null;
  campus?: { code?: string | null; name?: string | null } | null;
  schoolId?: string | null;
  school?: { id?: string | null; name?: string | null } | null;
  classId?: string | null;
  class?: { id?: string | null; name?: string | null } | null;
  /**
   * Drill-down override: when set (resolved + validated from the active-tenant
   * request header), scope resolution acts as this tenant instead of the user's
   * own. The full chain is resolved so leaf-getters return the right ids.
   */
  activeScope?: {
    level: 'organization' | 'school' | 'campus' | 'class';
    organizationId: string | null;
    schoolId: string | null;
    campusId: string | null;
    classId: string | 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;
}
