package com.mosquito.project.service;

import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Base64;

@Service
public class ApiKeyEncryptionService {

    private static final Logger log = LoggerFactory.getLogger(ApiKeyEncryptionService.class);

    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 128;
    private static final String DEFAULT_ENCRYPTION_KEY = "default-32-byte-key-for-dev-only!";
    private static final String LEGACY_DEFAULT_ENCRYPTION_KEY = "default-32-byte-key-for-dev-only!!";

    @Value("${app.security.encryption-key:default-32-byte-key-for-dev-only!}")
    private String encryptionKey;

    @Autowired(required = false)
    private Environment environment;

    private SecretKeySpec secretKey;
    private SecureRandom secureRandom;

    @PostConstruct
    public void init() {
        if (isProductionProfile() && isDefaultKey(encryptionKey)) {
            throw new IllegalStateException("Encryption key must be set in production");
        }
        byte[] keyBytes = encryptionKey.getBytes();
        if (keyBytes.length != 32) {
            byte[] paddedKey = new byte[32];
            System.arraycopy(keyBytes, 0, paddedKey, 0, Math.min(keyBytes.length, 32));
            keyBytes = paddedKey;
        }
        this.secretKey = new SecretKeySpec(keyBytes, "AES");
        this.secureRandom = new SecureRandom();
        log.info("ApiKeyEncryptionService initialized");
    }

    public String encrypt(String plainText) {
        if (plainText == null || plainText.isBlank()) {
            return null;
        }
        try {
            byte[] iv = new byte[GCM_IV_LENGTH];
            secureRandom.nextBytes(iv);

            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

            byte[] cipherText = cipher.doFinal(plainText.getBytes());

            ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
            byteBuffer.put(iv);
            byteBuffer.put(cipherText);

            return Base64.getEncoder().encodeToString(byteBuffer.array());
        } catch (Exception e) {
            log.error("Failed to encrypt API key", e);
            throw new RuntimeException("Encryption failed", e);
        }
    }

    public String decrypt(String encryptedText) {
        if (encryptedText == null || encryptedText.isBlank()) {
            return null;
        }
        try {
            byte[] decoded = Base64.getDecoder().decode(encryptedText);

            ByteBuffer byteBuffer = ByteBuffer.wrap(decoded);
            byte[] iv = new byte[GCM_IV_LENGTH];
            byteBuffer.get(iv);
            byte[] cipherText = new byte[byteBuffer.remaining()];
            byteBuffer.get(cipherText);

            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);

            byte[] plainText = cipher.doFinal(cipherText);
            return new String(plainText);
        } catch (Exception e) {
            log.error("Failed to decrypt API key", e);
            throw new RuntimeException("Decryption failed", e);
        }
    }

    private boolean isProductionProfile() {
        return environment != null && environment.acceptsProfiles(Profiles.of("prod"));
    }

    private boolean isDefaultKey(String key) {
        if (key == null || key.isBlank()) {
            return true;
        }
        return DEFAULT_ENCRYPTION_KEY.equals(key) || LEGACY_DEFAULT_ENCRYPTION_KEY.equals(key);
    }
}
