import type {
  BackofficeSessionRepository,
  BackofficeUserRepository,
  BackofficePasswordHasher,
  BackofficeLoginRateLimiter,
} from '../domain/ports.js';
import { BackofficeInvalidCredentialsError, BackofficeRateLimitedError } from '../domain/errors.js';
import type { BackofficeUserWithHash } from '../domain/backoffice-user.js';
import { backofficeSessionExpiry, type BackofficeSession } from '../domain/backoffice-session.js';

export interface BackofficeLoginInput {
  email: string;
  password: string;
}

export interface BackofficeLoginResult {
  user: BackofficeUserWithHash;
  session: BackofficeSession;
  token: string;
}

export class BackofficeLogin {
  constructor(
    private readonly deps: {
      users: BackofficeUserRepository;
      sessions: BackofficeSessionRepository;
      hasher: BackofficePasswordHasher;
      rateLimiter: BackofficeLoginRateLimiter;
      generateToken: () => string;
      hashToken: (token: string) => string;
    },
  ) {}

  async execute(input: BackofficeLoginInput): Promise<BackofficeLoginResult> {
    const email = input.email.trim().toLowerCase();
    const limitKey = `backoffice-login:${email}`;
    const decision = this.deps.rateLimiter.hit(limitKey);
    if (!decision.allowed) throw new BackofficeRateLimitedError(decision.retryAfterMs);

    const user = await this.deps.users.findByEmail(email);
    if (!user) throw new BackofficeInvalidCredentialsError();

    const ok = await this.deps.hasher.verify(user.passwordHash, input.password);
    if (!ok) throw new BackofficeInvalidCredentialsError();

    const token = this.deps.generateToken();
    const tokenHash = this.deps.hashToken(token);
    const session = await this.deps.sessions.create(user.id, tokenHash, backofficeSessionExpiry());

    return { user, session, token };
  }
}
