package com.gcsc.connection.apikey.service;

import com.gcsc.connection.apikey.dto.ApiKeyCreateResponse;
import com.gcsc.connection.apikey.dto.ApiKeyDetailResponse;
import com.gcsc.connection.apikey.dto.ApiKeyResponse;
import com.gcsc.connection.apikey.dto.CreateApiKeyRequest;
import com.gcsc.connection.apikey.entity.ApiKeyStatus;
import com.gcsc.connection.apikey.entity.SnpApiKey;
import com.gcsc.connection.apikey.repository.SnpApiKeyRepository;
import com.gcsc.connection.common.exception.BusinessException;
import com.gcsc.connection.common.exception.ErrorCode;
import com.gcsc.connection.common.util.AesEncryptor;
import com.gcsc.connection.user.entity.SnpUser;
import com.gcsc.connection.user.repository.SnpUserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.util.List;

/**
 * API Key 관리 서비스
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ApiKeyService {

    private static final int RAW_KEY_HEX_LENGTH = 32;
    private static final String KEY_PREFIX = "snp_";
    private static final int PREFIX_LENGTH = 8;

    private final SnpApiKeyRepository snpApiKeyRepository;
    private final SnpUserRepository snpUserRepository;
    private final AesEncryptor aesEncryptor;

    /**
     * 내 API Key 목록 조회
     */
    @Transactional(readOnly = true)
    public List<ApiKeyResponse> getMyKeys(Long userId) {
        return snpApiKeyRepository.findByUserUserId(userId).stream()
            .map(ApiKeyResponse::from)
            .toList();
    }

    /**
     * API Key 상세 조회 (복호화된 키 포함)
     */
    @Transactional(readOnly = true)
    public ApiKeyDetailResponse getKeyDetail(Long keyId) {
        SnpApiKey apiKey = snpApiKeyRepository.findById(keyId)
            .orElseThrow(() -> new BusinessException(ErrorCode.API_KEY_NOT_FOUND));
        String decryptedKey = aesEncryptor.decrypt(apiKey.getApiKey());
        return ApiKeyDetailResponse.from(apiKey, decryptedKey);
    }

    /**
     * API Key 생성
     */
    @Transactional
    public ApiKeyCreateResponse createKey(Long userId, CreateApiKeyRequest request) {
        SnpUser user = snpUserRepository.findById(userId)
            .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        String rawKey = generateRawKey();
        String prefix = rawKey.substring(0, PREFIX_LENGTH);
        String encryptedKey = aesEncryptor.encrypt(rawKey);

        SnpApiKey apiKey = SnpApiKey.builder()
            .user(user)
            .apiKey(encryptedKey)
            .apiKeyPrefix(prefix)
            .keyName(request.keyName())
            .status(ApiKeyStatus.ACTIVE)
            .dailyRequestLimit(request.dailyRequestLimit())
            .build();

        SnpApiKey saved = snpApiKeyRepository.save(apiKey);

        return new ApiKeyCreateResponse(
            saved.getApiKeyId(),
            saved.getKeyName(),
            rawKey,
            saved.getApiKeyPrefix(),
            saved.getStatus().name()
        );
    }

    /**
     * API Key 폐기
     */
    @Transactional
    public void revokeKey(Long keyId) {
        SnpApiKey apiKey = snpApiKeyRepository.findById(keyId)
            .orElseThrow(() -> new BusinessException(ErrorCode.API_KEY_NOT_FOUND));

        if (apiKey.getStatus() == ApiKeyStatus.REVOKED) {
            throw new BusinessException(ErrorCode.API_KEY_ALREADY_REVOKED);
        }

        apiKey.revoke();
    }

    /**
     * 전체 API Key 목록 조회 (관리자용)
     */
    @Transactional(readOnly = true)
    public List<ApiKeyResponse> getAllKeys() {
        return snpApiKeyRepository.findAll().stream()
            .map(ApiKeyResponse::from)
            .toList();
    }

    /**
     * 랜덤 API Key 문자열 생성
     */
    private String generateRawKey() {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[RAW_KEY_HEX_LENGTH / 2];
        random.nextBytes(bytes);
        StringBuilder hex = new StringBuilder(KEY_PREFIX);
        for (byte b : bytes) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }
}
