import {
  Entity,
  Column,
  ManyToOne,
  JoinColumn,
  Index,
  PrimaryGeneratedColumn,
  CreateDateColumn,
} from 'typeorm';
import { UserEntity } from './user.entity';

/**
 * RefreshTokenEntity — one row per issued refresh token.
 *
 * The raw token is never persisted. Only its SHA-256 hex digest is
 * stored, so a stolen DB dump cannot be redeemed. Lookups happen by
 * hash + user_id + tenant_id.
 *
 * Rotation model: single-use. When the client hits POST /auth/refresh,
 * the incoming token's row is marked used_at and a fresh row is issued.
 * If the same used token is presented a second time, treat it as a
 * theft signal and revoke the whole user's token family (see
 * `revoked_family_id`), forcing re-login. This is the OWASP "refresh
 * token rotation with reuse detection" pattern.
 *
 * Not extending BaseEntity for the same reasons as password_resets:
 * the refresh endpoint is unauthenticated (that's the whole point) so
 * CLS-scoped auto columns don't apply here.
 */
@Entity('refresh_tokens')
@Index('IDX_refresh_tokens_token_hash', ['token_hash'], {
  unique: true,
  where: '"is_deleted" = false',
})
@Index('IDX_refresh_tokens_user_active', ['user_id'], {
  where: '"used_at" IS NULL AND "is_deleted" = false',
})
@Index('IDX_refresh_tokens_family', ['family_id'])
export class RefreshTokenEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ type: 'uuid' })
  tenant_id: string;

  @ManyToOne(() => UserEntity, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'user_id' })
  user: UserEntity;

  @Column({ type: 'uuid' })
  user_id: string;

  /**
   * Family id — every rotation of the same original refresh token
   * carries the same family_id. On reuse detection we revoke the
   * whole family so an attacker holding a stolen (already-rotated)
   * token can't ride the new one either.
   */
  @Column({ type: 'uuid' })
  family_id: string;

  /** SHA-256 hex digest of the raw refresh token. */
  @Column({ type: 'varchar', length: 64 })
  token_hash: string;

  @Column({ type: 'timestamptz' })
  expires_at: Date;

  /** Filled the moment this token is rotated. */
  @Column({ type: 'timestamptz', nullable: true })
  used_at: Date | null;

  /** Filled if the family was force-revoked (reuse detected or logout). */
  @Column({ type: 'timestamptz', nullable: true })
  revoked_at: Date | null;

  @Column({ type: 'varchar', length: 45, nullable: true })
  issued_ip: string | null;

  @Column({ type: 'text', nullable: true })
  issued_user_agent: string | null;

  @CreateDateColumn({ type: 'timestamptz' })
  created_at: Date;

  @Column({ type: 'boolean', default: false })
  is_deleted: boolean;
}
