package com.erp.ai.service;

import com.erp.ai.dto.GroupMemberRequest;
import com.erp.ai.dto.GroupRequest;
import com.erp.ai.exception.AiAgentException;
import com.erp.ai.mapper.AiAgentGroupMapper;
import com.erp.ai.mapper.AiAgentGroupMemberMapper;
import com.erp.ai.model.AiAgentGroup;
import com.erp.ai.model.AiAgentGroupMember;
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.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * AI 에이전트 그룹 서비스.
 * vexplor aiAgentGroupService.ts 1:1 포팅.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AiAgentGroupService {

    private final AiAgentGroupMapper groupMapper;
    private final AiAgentGroupMemberMapper memberMapper;

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

    public List<AiAgentGroup> list(String companyCode) {
        return groupMapper.list(companyCode);
    }

    public Map<String, Object> getById(long id) {
        AiAgentGroup group = groupMapper.getById(id);
        if (group == null) return null;
        List<AiAgentGroupMember> members = memberMapper.listByGroupId(id);
        Map<String, Object> result = new HashMap<>();
        result.put("id", group.getId());
        result.put("group_id", group.getGroup_id());
        result.put("name", group.getName());
        result.put("description", group.getDescription());
        result.put("execution_mode", group.getExecution_mode());
        result.put("status", group.getStatus());
        result.put("company_code", group.getCompany_code());
        result.put("created_by", group.getCreated_by());
        result.put("created_at", group.getCreated_at());
        result.put("updated_at", group.getUpdated_at());
        result.put("members", members.stream().map(this::memberToResponseMap).toList());
        return result;
    }

    /**
     * AiAgentGroupMember → API 응답용 Map.
     * JSONB(::text) 컬럼 connectors / config 를 Object 로 파싱.
     * MultiAgentExecutionEngine 의 raw String 사용처는 model 을 직접 사용하므로 영향 없음.
     */
    private Map<String, Object> memberToResponseMap(AiAgentGroupMember m) {
        Map<String, Object> row = new HashMap<>();
        row.put("id", m.getId());
        row.put("group_id", m.getGroup_id());
        row.put("agent_id", m.getAgent_id());
        row.put("role_name", m.getRole_name());
        row.put("connectors", m.getConnectors());
        row.put("execution_order", m.getExecution_order());
        row.put("config", m.getConfig());
        row.put("created_at", m.getCreated_at());
        row.put("updated_at", m.getUpdated_at());
        row.put("agent_name", m.getAgent_name());
        row.put("agent_model", m.getAgent_model());
        parseJsonField(row, "connectors");
        parseJsonField(row, "config");
        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 AiAgentGroup getEntityById(long id) {
        return groupMapper.getById(id);
    }

    public AiAgentGroup getByGroupId(String groupId) {
        return groupMapper.getByGroupId(groupId);
    }

    public List<AiAgentGroupMember> listMembers(long groupId) {
        return memberMapper.listByGroupId(groupId);
    }

    @Transactional
    public AiAgentGroup create(GroupRequest req, String userId) {
        AiAgentGroup group = new AiAgentGroup();
        group.setGroup_id("group-" + Long.toString(System.currentTimeMillis(), 36));
        group.setName(req.getName());
        group.setDescription(req.getDescription());
        group.setExecution_mode(req.getExecution_mode() != null ? req.getExecution_mode() : "mixed");
        group.setStatus("active");
        group.setCompany_code(req.getCompany_code());
        group.setCreated_by(userId);
        groupMapper.insert(group);
        log.info("멀티 에이전트 그룹 생성: {} (by {})", req.getName(), userId);
        return groupMapper.getById(group.getId());
    }

    @Transactional
    public AiAgentGroup update(long id, GroupRequest req) {
        AiAgentGroup existing = groupMapper.getById(id);
        if (existing == null) return null;

        AiAgentGroup g = new AiAgentGroup();
        g.setId(id);
        g.setName(req.getName());
        g.setDescription(req.getDescription());
        g.setExecution_mode(req.getExecution_mode());
        g.setStatus(req.getStatus());
        groupMapper.update(g);
        return groupMapper.getById(id);
    }

    @Transactional
    public void delete(long id) {
        groupMapper.softDelete(id);
    }

    // ===== 멤버 관리 =====

    @Transactional
    public AiAgentGroupMember addMember(long groupId, GroupMemberRequest req) {
        if (req.getAgent_id() == null) throw new AiAgentException("agent_id 가 필요합니다.");
        AiAgentGroupMember member = new AiAgentGroupMember();
        member.setGroup_id(groupId);
        member.setAgent_id(req.getAgent_id());
        member.setRole_name(req.getRole_name());
        member.setConnectors(toJsonString(req.getConnectors() != null ? req.getConnectors() : List.of()));
        member.setExecution_order(req.getExecution_order() != null ? req.getExecution_order() : 1);
        member.setConfig(toJsonString(req.getConfig() != null ? req.getConfig() : Map.of()));
        memberMapper.insert(member);
        groupMapper.touchUpdatedAt(groupId);
        return memberMapper.getById(member.getId());
    }

    @Transactional
    public AiAgentGroupMember updateMember(long memberId, GroupMemberRequest req) {
        AiAgentGroupMember m = new AiAgentGroupMember();
        m.setId(memberId);
        m.setRole_name(req.getRole_name());
        if (req.getConnectors() != null) m.setConnectors(toJsonString(req.getConnectors()));
        m.setExecution_order(req.getExecution_order());
        if (req.getConfig() != null) m.setConfig(toJsonString(req.getConfig()));
        memberMapper.update(m);
        return memberMapper.getById(memberId);
    }

    @Transactional
    public void removeMember(long memberId) {
        memberMapper.delete(memberId);
    }

    /**
     * 사용 가능한 커넥터 목록.
     * vexplor 의 try/catch + empty fallback 동일.
     * invyone에는 external_db_connections 등 테이블이 없어 빈 배열 반환.
     */
    public List<Map<String, Object>> getAvailableConnectors() {
        // architecture §3.13: invyone에 부재 — 빈 배열 fallback.
        return List.of();
    }

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