import { NextFunction, Response } from "express";

import passport from "passport";
import { ExtractJwt, Strategy as JwtStrategy } from "passport-jwt";
import { TabContext } from "../../../../../../apps/server/archive/contexts/auth/domain";
import {
  IAuthService,
  ITabContextService,
} from "../../../../../../apps/server/archive/contexts/auth/domain/services";
import { TabContextRequest } from "../../../../../../apps/server/archive/contexts/auth/infraestructure/express/types";

const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";

export class PassportAuthProvider {
  private async _getContextByTabId(value: string): Promise<Result<TabContext, Error>> {
    const tabIdOrError = UniqueID.create(value, false);
    if (tabIdOrError.isFailure) {
      return Result.fail(new Error("Invalid tab ID"));
    }

    const tabResult = await this.tabContextService.getContextByTabId(tabIdOrError.data);
    if (tabResult.isFailure) {
      return Result.fail(new Error("Invalid token data"));
    }

    return Result.ok(tabResult.data);
  }

  private async _getUserByToken(tokenPayload: any) {
    const { user_id, email, roles } = tokenPayload;

    const userIdVO = UniqueID.create(user_id);
    const emailVO = EmailAddress.create(email!);

    const okOrError = Result.combine([userIdVO, emailVO]);
    if (okOrError.isFailure) {
      return Result.fail(okOrError.error.message);
    }

    const userResult = await this.authService.getUserByEmail(emailVO.data);

    if (userResult.isFailure) {
      return Result.fail(new Error("Invalid token data"));
    }

    const user = userResult.data;

    const checkUserId = user.id.equals(userIdVO.data);
    const checkRoles = true; //user.hasRoles(roles);

    if (!checkUserId || !checkRoles) {
      return Result.fail(new Error("Invalid token data"));
    }

    return Result.ok(user);
  }

  constructor(
    private readonly authService: IAuthService,
    private readonly tabContextService: ITabContextService,
    private readonly transactionManager: ITransactionManager
  ) {}

  /**
   * 🔹 Configura PassportJS
   */
  initialize(): void {
    const jwtOptions = {
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: SECRET_KEY,
    };

    passport.use(
      "jwt",
      new JwtStrategy(jwtOptions, async (tokenPayload, done) => {
        try {
          const userOrError = await this._getUserByToken(tokenPayload);
          if (userOrError.isFailure) {
            return done(userOrError.error, false, { message: "Invalid JWT data" });
          }

          return done(null, userOrError.data);
        } catch (error) {
          return done(error, false);
        }
      })
    );

    passport.initialize();
  }

  authenticateJWT() {
    logger.debug("Authenticating JWT");
    return passport.authenticate("jwt", { session: false });
  }

  authenticateTabId() {
    logger.debug("Authenticating Tab ID");
    return async (req: TabContextRequest, res: Response, next: NextFunction) => {
      const tabIdValue = req.header("X-Tab-ID");
      if (!tabIdValue) {
        return res.status(401).json({ message: "Tab ID is required" });
      }

      const tabContextOrError = await this._getContextByTabId(tabIdValue);
      if (tabContextOrError.isFailure) {
        return res.status(401).json({ message: "Invalid tab context data" });
      }

      req.tabContext = tabContextOrError.data;
      return next();
    };
  }
}
