import "reflect-metadata";
import { inject, injectable } from "inversify";

import type { IAuthenticationService } from "../../application/services/authentication.service.interface";
import type { IUsersRepository } from "../../application/repositories/users.repository.interface";
import { UnauthenticatedError } from "../../entities/errors/auth";
import { sessionSchema, type Session } from "../../entities/models/session";
import type { Cookie } from "../../entities/models/cookie";
import type { User } from "../../entities/models/user";
import { AUTH_SYMBOLS } from "../../di/symbols";
import { SESSION_COOKIE } from "../../config";

@injectable()
export class MockAuthenticationService implements IAuthenticationService {
  private _sessions: Record<string, { session: Session; user: User }> = {};

  constructor(
    @inject(AUTH_SYMBOLS.IUsersRepository)
    private _usersRepository: IUsersRepository,
  ) {}

  generateUserId(): string {
    return (Math.random() + 1).toString(36).substring(7);
  }

  async hashPassword(password: string): Promise<string> {
    return `hashed_${password}`;
  }

  async verifyPassword(hash: string, password: string): Promise<boolean> {
    return hash === `hashed_${password}`;
  }

  async validateSession(
    sessionId: string,
  ): Promise<{ user: User; session: Session }> {
    const result = this._sessions[sessionId];
    if (!result) {
      throw new UnauthenticatedError("Unauthenticated");
    }
    const user = await this._usersRepository.getUser(result.user.id);
    if (!user) {
      throw new UnauthenticatedError("Unauthenticated");
    }
    return { user, session: result.session };
  }

  async createSession(
    user: User,
  ): Promise<{ session: Session; cookie: Cookie }> {
    const session = sessionSchema.parse({
      id: "session_" + user.id,
      userId: user.id,
      expiresAt: new Date(Date.now() + 86400000 * 7),
    });
    const cookie: Cookie = {
      name: SESSION_COOKIE,
      value: session.id,
      attributes: {},
    };
    this._sessions[session.id] = { session, user };
    return { session, cookie };
  }

  async invalidateSession(
    sessionId: string,
  ): Promise<{ blankCookie: Cookie }> {
    delete this._sessions[sessionId];
    return {
      blankCookie: { name: SESSION_COOKIE, value: "", attributes: {} },
    };
  }
}
