package com.erp.ai.service;

import com.erp.ai.dto.ApiKeyCreateRequest;
import com.erp.ai.exception.AiAgentException;
import com.erp.ai.mapper.AiAgentApiKeyMapper;
import com.erp.ai.model.AiAgentApiKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;

/**
 * AI API 키 관리 서비스.
 * vexplor aiAgentApiKeyService.ts 1:1 포팅.
 * - sk-pipe-{64hex} 발급
 * - SHA-256 hash 저장 (plain key는 1회 반환 후 폐기)
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AiAgentApiKeyService {

    private final AiAgentApiKeyMapper apiKeyMapper;
    private final SecureRandom secureRandom = new SecureRandom();

    @Qualifier("aiObjectMapper")
    private final ObjectMapper objectMapper;

    public List<Map<String, Object>> list(String userId, boolean isAdmin) {
        List<AiAgentApiKey> keys = isAdmin ? apiKeyMapper.listAll() : apiKeyMapper.listByUser(userId);
        return keys.stream().map(this::toResponseMap).toList();
    }

    private Map<String, Object> toResponseMap(AiAgentApiKey k) {
        Map<String, Object> row = new HashMap<>();
        row.put("id", k.getId());
        row.put("name", k.getName());
        row.put("key_prefix", k.getKey_prefix());
        row.put("user_id", k.getUser_id());
        row.put("company_code", k.getCompany_code());
        row.put("agent_id", k.getAgent_id());
        row.put("permissions", k.getPermissions());
        row.put("rate_limit", k.getRate_limit());
        row.put("monthly_token_limit", k.getMonthly_token_limit());
        row.put("status", k.getStatus());
        row.put("last_used_at", k.getLast_used_at());
        row.put("usage_count", k.getUsage_count());
        row.put("total_tokens", k.getTotal_tokens());
        row.put("expires_at", k.getExpires_at());
        row.put("created_at", k.getCreated_at());
        parseJsonField(row, "permissions");
        return row;
    }

    private void parseJsonField(Map<String, Object> row, String key) {
        Object val = row.get(key);
        if (val instanceof String s && !s.isBlank()) {
            try {
                row.put(key, objectMapper.readValue(s, Object.class));
            } catch (Exception e) {
                log.warn("Failed to parse JSONB field '{}': {}", key, e.getMessage());
            }
        }
    }

    public AiAgentApiKey getById(long id) {
        return apiKeyMapper.getById(id);
    }

    /** {key, plainKey} 쌍 반환 */
    public CreatedKey create(ApiKeyCreateRequest req, String userId, String companyCode) {
        GeneratedKey gen = generateKey();

        AiAgentApiKey key = new AiAgentApiKey();
        key.setName(req.getName());
        key.setKey_hash(gen.hash);
        key.setKey_prefix(gen.prefix);
        key.setUser_id(userId);
        key.setCompany_code(companyCode);
        key.setAgent_id(req.getAgent_id());
        key.setPermissions(toJsonString(req.getPermissions() != null ? req.getPermissions() : List.of("chat")));
        key.setRate_limit(req.getRate_limit() != null ? req.getRate_limit() : 60);
        key.setMonthly_token_limit(req.getMonthly_token_limit() != null ? req.getMonthly_token_limit() : 1_000_000L);
        key.setExpires_at(req.getExpires_at());

        apiKeyMapper.insert(key);
        AiAgentApiKey saved = apiKeyMapper.getById(key.getId());
        log.info("API 키 생성: {}... (by {})", gen.prefix, userId);
        return new CreatedKey(saved, gen.plainKey);
    }

    @Transactional
    public void revoke(long id, String userId) {
        apiKeyMapper.delete(id, userId);
        log.info("API 키 삭제: id={} (by {})", id, userId);
    }

    /**
     * 외부 API 키 검증 — SHA-256 매칭 + 만료 체크 + last_used 갱신.
     * @return null = 무효, 객체 = 유효
     */
    @Transactional
    public AiAgentApiKey validateKey(String plainKey) {
        if (plainKey == null || !plainKey.startsWith("sk-pipe-")) return null;
        String hash = sha256Hex(plainKey);
        AiAgentApiKey key = apiKeyMapper.getByKeyHash(hash);
        if (key == null) return null;
        if (key.getExpires_at() != null && key.getExpires_at().isBefore(OffsetDateTime.now())) {
            return null;
        }
        apiKeyMapper.updateLastUsed(key.getId());
        return key;
    }

    public void addTokenUsage(long keyId, long tokens) {
        apiKeyMapper.addTokenUsage(keyId, tokens);
    }

    public int countActive() {
        return apiKeyMapper.countActive();
    }

    private GeneratedKey generateKey() {
        byte[] randomBytes = new byte[32];
        secureRandom.nextBytes(randomBytes);
        String hex = HexFormat.of().formatHex(randomBytes);
        String plainKey = "sk-pipe-" + hex;
        String hash = sha256Hex(plainKey);
        String prefix = plainKey.substring(0, 16);
        return new GeneratedKey(plainKey, hash, prefix);
    }

    private String sha256Hex(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return HexFormat.of().formatHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new AiAgentException("SHA-256 미지원", e);
        }
    }

    private String toJsonString(Object value) {
        if (value == null) return null;
        try {
            return objectMapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new AiAgentException("JSON 직렬화 실패", e);
        }
    }

    public record CreatedKey(AiAgentApiKey key, String plainKey) {}
    private record GeneratedKey(String plainKey, String hash, String prefix) {}
}
