package com.erp.service;

import com.erp.common.BaseService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 버튼 데이터플로우 서비스
 *
 * Node.js buttonDataflowController 포팅.
 * - 관계도/관계 조회  → DataflowDiagramService 위임
 * - 조인 관계 조회    → ButtonDataflowMapper (table_relationships 양방향 쿼리)
 * - 버튼 실행 패턴    → 즉시 응답 + jobId (Node 코드 대부분 TODO/mock 이므로 동일 구조 구현)
 */
@Service
@Slf4j
public class ButtonDataflowService extends BaseService {

    @Autowired
    private DataflowDiagramService diagramService;
    @Autowired
    private ObjectMapper objectMapper;

    // ══════════════════════════════════════════════════════════════
    // 1. 버튼 설정 (mock — Node 도 TODO/hardcoded)
    // ══════════════════════════════════════════════════════════════

    public Map<String, Object> getButtonDataflowConfig(String buttonId) {
        // Node 코드와 동일한 mock 구조
        Map<String, Object> config = new LinkedHashMap<>();
        config.put("control_mode", "simple");
        config.put("selected_diagram_id", 1);
        config.put("selected_relationship_id", "rel-123");
        Map<String, Object> opts = new LinkedHashMap<>();
        opts.put("rollback_on_error", true);
        opts.put("enable_logging", true);
        opts.put("async_execution", true);
        config.put("execution_options", opts);
        return config;
    }

    public void updateButtonDataflowConfig(String buttonId, Map<String, Object> config) {
        log.info("Button dataflow config updated: buttonId={}, config={}", buttonId, config);
        // Node 코드와 동일 — DB 저장 TODO
    }

    // ══════════════════════════════════════════════════════════════
    // 2. 관계도 목록 조회
    // ══════════════════════════════════════════════════════════════

    public List<Map<String, Object>> getAvailableDiagrams(String companyCode) {
        Map<String, Object> params = new LinkedHashMap<>();
        params.put("company_code", companyCode);
        params.put("page",  1);
        params.put("size",  100);

        Map<String, Object> result = diagramService.getDataflowDiagrams(params);

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> diagrams = (List<Map<String, Object>>) result.get("diagrams");
        return diagrams != null ? diagrams : Collections.emptyList();
    }

    // ══════════════════════════════════════════════════════════════
    // 3. 특정 관계도의 관계 목록
    // ══════════════════════════════════════════════════════════════

    public List<Map<String, Object>> getDiagramRelationships(int diagramId, String companyCode) {
        Map<String, Object> diagram = diagramService.getDataflowDiagramById(diagramId, companyCode);
        if (diagram == null) return Collections.emptyList();

        // relationships 는 { relationships: [...], ... } 구조의 JSONB
        Map<String, Object> relObj = parseToMap(diagram.get("relationships"));

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> list = (List<Map<String, Object>>) relObj.get("relationships");
        return list != null ? list : Collections.emptyList();
    }

    // ══════════════════════════════════════════════════════════════
    // 4. 관계 미리보기
    // ══════════════════════════════════════════════════════════════

    public Map<String, Object> getRelationshipPreview(int diagramId, String relationshipId, String companyCode) {
        Map<String, Object> diagram = diagramService.getDataflowDiagramById(diagramId, companyCode);
        if (diagram == null) return null;

        // relationships JSONB 파싱
        Map<String, Object> relObj = parseToMap(diagram.get("relationships"));
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> relationships = (List<Map<String, Object>>) relObj.get("relationships");
        if (relationships == null) relationships = Collections.emptyList();

        // control / plan JSONB 파싱 (배열)
        List<Map<String, Object>> controlList = parseToList(diagram.get("control"));
        List<Map<String, Object>> planList    = parseToList(diagram.get("plan"));

        // 지정된 ID로 관계 탐색
        Map<String, Object> relationship = findById(relationships, relationshipId);

        // Node 코드와 동일: 없으면 첫 번째 관계를 fallback
        if (relationship == null && !relationships.isEmpty()) {
            log.warn("관계 ID '{}' 미발견, fallback → 첫 번째 관계 사용", relationshipId);
            relationship = relationships.get(0);
        }
        if (relationship == null) return null;

        String effectiveId = (String) relationship.get("id");

        Map<String, Object> control = findById(controlList, effectiveId);
        Map<String, Object> plan    = findById(planList,    effectiveId);

        int conditionsCount = countNested(control, "conditions");
        int actionsCount    = countNested(plan,    "actions");

        Map<String, Object> preview = new LinkedHashMap<>();
        preview.put("relationship",      relationship);
        preview.put("control",           control);
        preview.put("plan",              plan);
        preview.put("conditions_count",   conditionsCount);
        preview.put("actions_count",      actionsCount);
        return preview;
    }

    // ══════════════════════════════════════════════════════════════
    // 5. 버튼 실행 (최적화 / 단순 / 레거시)
    // ══════════════════════════════════════════════════════════════

    /**
     * 최적화 버튼 실행 (Node 코드 executeOptimizedButton 동일 구조)
     * timing: after | before | replace
     */
    public Map<String, Object> executeOptimizedButton(Map<String, Object> params) {
        String buttonId   = (String) params.get("button_id");
        String actionType = (String) params.get("action_type");
        String timing     = params.get("timing") != null ? (String) params.get("timing") : "after";

        long startTime = System.currentTimeMillis();

        // jobId 생성
        String jobId = "job_" + System.currentTimeMillis() + "_"
                + UUID.randomUUID().toString().replace("-", "").substring(0, 9);

        Map<String, Object> immediateResult = new LinkedHashMap<>();
        immediateResult.put("success",   true);
        immediateResult.put("message",   actionType + " 액션이 완료되었습니다.");
        immediateResult.put("action_type", actionType);
        immediateResult.put("timestamp", new java.util.Date().toString());

        long responseTime = System.currentTimeMillis() - startTime;
        log.info("버튼 실행 ({}): {}ms, jobId={}", timing, responseTime, jobId);

        Map<String, Object> data = new LinkedHashMap<>();
        data.put("job_id",           jobId);
        data.put("immediate_result", immediateResult);
        data.put("is_background",    true);
        data.put("timing",          timing);
        data.put("response_time",    responseTime);
        return data;
    }

    /**
     * 단순 데이터플로우 즉시 실행 (Node 코드 executeSimpleDataflow 동일 구조)
     */
    public Map<String, Object> executeSimpleDataflow(Map<String, Object> params) {
        String companyCode = (String) params.getOrDefault("company_code", "*");
        log.info("단순 데이터플로우 실행: companyCode={}", companyCode);

        // Node 코드와 동일한 응답 구조
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("success",         true);
        result.put("executed_actions", 0);
        result.put("message",         "0개의 액션이 실행되었습니다.");
        result.put("results",         Collections.emptyList());
        return result;
    }

    // ══════════════════════════════════════════════════════════════
    // 6. 작업 상태 조회 (mock)
    // ══════════════════════════════════════════════════════════════

    public Map<String, Object> getJobStatus(String jobId) {
        // Node 코드와 동일한 mock 응답
        Map<String, Object> innerResult = new LinkedHashMap<>();
        innerResult.put("success",          true);
        innerResult.put("executed_actions",  2);
        innerResult.put("message",          "백그라운드 처리가 완료되었습니다.");

        Map<String, Object> status = new LinkedHashMap<>();
        status.put("status",   "completed");
        status.put("result",   innerResult);
        status.put("progress", 100);
        return status;
    }

    // ══════════════════════════════════════════════════════════════
    // 7. 조인 관계 조회 (table_relationships 양방향)
    // ══════════════════════════════════════════════════════════════

    public Map<String, Object> getJoinRelationship(String mainTable, String detailTable, String companyCode) {
        log.info("조인 관계 조회: {}.* → {}.*  (company={})", mainTable, detailTable, companyCode);

        Map<String, Object> qParams = new HashMap<>();
        qParams.put("main_table",   mainTable);
        qParams.put("detail_table", detailTable);
        qParams.put("company_code", companyCode != null ? companyCode : "*");

        Map<String, Object> row = sqlSession.selectOne("buttonDataflow.getButtonDataflowJoinRelationship", qParams);

        if (row == null) {
            log.info("조인 관계 없음: {} ↔ {}", mainTable, detailTable);
            return Map.of("found", false);
        }

        // 방향에 따라 컬럼 매핑 결정 (Node 동일 로직)
        String fromTable  = (String) row.get("from_table_name");
        String fromColumn = (String) row.get("from_column_name");
        String toColumn   = (String) row.get("to_column_name");

        String mainColumn, detailColumn;
        if (mainTable.equals(fromTable)) {
            mainColumn   = fromColumn;
            detailColumn = toColumn;
        } else {
            mainColumn   = toColumn;
            detailColumn = fromColumn;
        }

        // 다중 컬럼(쉼표 구분) → 첫 번째만 사용
        if (mainColumn != null && mainColumn.contains(","))   mainColumn   = mainColumn.split(",")[0].trim();
        if (detailColumn != null && detailColumn.contains(",")) detailColumn = detailColumn.split(",")[0].trim();

        log.info("조인 관계 발견: {}.{} → {}.{}", mainTable, mainColumn, detailTable, detailColumn);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("found",            true);
        result.put("main_column",       mainColumn);
        result.put("detail_column",     detailColumn);
        result.put("relationship_type", row.get("relationship_type"));
        return result;
    }

    // ══════════════════════════════════════════════════════════════
    // Private helpers
    // ══════════════════════════════════════════════════════════════

    @SuppressWarnings("unchecked")
    private Map<String, Object> parseToMap(Object raw) {
        if (raw == null) return Collections.emptyMap();
        if (raw instanceof Map) return (Map<String, Object>) raw;
        try {
            return objectMapper.readValue(raw.toString(), Map.class);
        } catch (Exception e) {
            log.warn("JSON→Map 파싱 실패: {}", e.getMessage());
            return Collections.emptyMap();
        }
    }

    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> parseToList(Object raw) {
        if (raw == null) return Collections.emptyList();
        if (raw instanceof List) return (List<Map<String, Object>>) raw;
        try {
            return objectMapper.readValue(raw.toString(), List.class);
        } catch (Exception e) {
            log.warn("JSON→List 파싱 실패: {}", e.getMessage());
            return Collections.emptyList();
        }
    }

    private Map<String, Object> findById(List<Map<String, Object>> list, String id) {
        if (list == null || id == null) return null;
        return list.stream()
                .filter(m -> id.equals(m.get("id")))
                .findFirst()
                .orElse(null);
    }

    @SuppressWarnings("unchecked")
    private int countNested(Map<String, Object> parent, String key) {
        if (parent == null) return 0;
        Object val = parent.get(key);
        if (val instanceof List) return ((List<?>) val).size();
        return 0;
    }
}
