package com.cloudhandson.ddsbackoffice.service;

import com.cloudhandson.ddsbackoffice.domain.DdsMcpAuthenticatedUser;
import com.cloudhandson.vpdbackoffice.domain.token.BearerTokenRecord;
import com.cloudhandson.vpdbackoffice.domain.user.AppUser;
import com.cloudhandson.vpdbackoffice.mapper.UserMapper;
import com.cloudhandson.vpdbackoffice.service.AppException;
import com.cloudhandson.vpdbackoffice.service.BearerTokenService;
import java.time.Clock;
import java.time.LocalDateTime;
import java.time.ZoneId;
import org.springframework.stereotype.Service;

/** Revalidates the MCP request bearer before every tool invocation. */
@Service
public class DdsMcpBearerAuthenticator {

  private static final int MAX_TOKEN_LENGTH = 4096;

  private final BearerTokenService bearerTokenService;
  private final UserMapper userMapper;
  private final DdsMcpEndUserResolver endUserResolver;
  private final Clock clock;

  public DdsMcpBearerAuthenticator(
      BearerTokenService bearerTokenService,
      UserMapper userMapper,
      DdsMcpEndUserResolver endUserResolver,
      Clock clock
  ) {
    this.bearerTokenService = bearerTokenService;
    this.userMapper = userMapper;
    this.endUserResolver = endUserResolver;
    this.clock = clock;
  }

  public DdsMcpAuthenticatedUser authenticate(String authorization) {
    String plainToken = bearerValue(authorization);
    BearerTokenRecord token = bearerTokenService.findByPlainToken(plainToken);
    LocalDateTime now = LocalDateTime.now(clock.withZone(ZoneId.systemDefault()));
    if (token == null || !token.active(now) || !bearerTokenService.matches(token, plainToken)) {
      throw denied();
    }
    AppUser user = userMapper.findById(token.userId());
    if (user == null || !user.active()) {
      throw denied();
    }
    return new DdsMcpAuthenticatedUser(user.userId(), user.username(), endUserResolver.resolve(user.userId()));
  }

  private String bearerValue(String authorization) {
    if (authorization == null || !authorization.regionMatches(true, 0, "Bearer ", 0, 7)) {
      throw denied();
    }
    String value = authorization.substring(7).trim();
    if (value.isEmpty() || value.length() > MAX_TOKEN_LENGTH) {
      throw denied();
    }
    return value;
  }

  private AppException denied() {
    return new AppException("AUTHORIZATION_DENIED: MCP Bearer를 확인할 수 없습니다.");
  }
}
