package com.erp.service;

import com.erp.common.BaseService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class WebTypeStandardService extends BaseService {

    private final ObjectMapper objectMapper;

    // input params (camelCase) → DB 저장 시 직렬화용
    private static final List<String> JSONB_FIELDS =
            Arrays.asList("default_config", "validation_rules", "default_style", "input_properties");

    // DB 컬럼명(snake_case) → 응답 필드명(camelCase) 매핑
    private static final Map<String, String> JSONB_DB_TO_RESPONSE = new LinkedHashMap<>();
    static {
        JSONB_DB_TO_RESPONSE.put("default_config",    "default_config");
        JSONB_DB_TO_RESPONSE.put("validation_rules",  "validation_rules");
        JSONB_DB_TO_RESPONSE.put("default_style",     "default_style");
        JSONB_DB_TO_RESPONSE.put("input_properties",  "input_properties");
    }

    // ── 목록 조회 ──────────────────────────────────────────────────
    public List<Map<String, Object>> getWebTypes(Map<String, Object> params) {
        // ILIKE 검색을 위해 % 와일드카드 추가
        if (params.get("search") != null) {
            params.put("search", "%" + params.get("search") + "%");
        }
        List<Map<String, Object>> rows = sqlSession.selectList(
                "webTypeStandard.selectWebTypes", params);
        rows.forEach(this::parseJsonFields);
        return rows;
    }

    // ── 카테고리 목록 ──────────────────────────────────────────────
    public List<Map<String, Object>> getWebTypeCategories() {
        return sqlSession.selectList("webTypeStandard.selectCategories", null);
    }

    // ── 단건 조회 ──────────────────────────────────────────────────
    public Map<String, Object> getWebType(String webType) {
        Map<String, Object> row = sqlSession.selectOne(
                "webTypeStandard.selectWebType", Map.of("web_type", webType));
        if (row != null) parseJsonFields(row);
        return row;
    }

    // ── 생성 ───────────────────────────────────────────────────────
    @Transactional
    public Map<String, Object> createWebType(Map<String, Object> params) {
        String webType = (String) params.get("web_type");

        // 중복 체크
        Map<String, Object> existing = sqlSession.selectOne(
                "webTypeStandard.selectWebType", Map.of("web_type", webType));
        if (existing != null) {
            throw new IllegalStateException("DUPLICATE");
        }

        // 기본값 적용
        params.putIfAbsent("category",      "input");
        params.putIfAbsent("component_name", "TextWidget");
        params.putIfAbsent("sort_order",     0);
        params.putIfAbsent("is_active",      "Y");

        // JSONB 직렬화
        serializeJsonbFields(params);

        sqlSession.insert("webTypeStandard.insertWebType", params);
        log.info("웹타입 생성: web_type={}", webType);

        Map<String, Object> result = sqlSession.selectOne(
                "webTypeStandard.selectWebType", Map.of("web_type", webType));
        if (result != null) parseJsonFields(result);
        return result;
    }

    // ── 수정 ───────────────────────────────────────────────────────
    @Transactional
    public Map<String, Object> updateWebType(Map<String, Object> params) {
        // JSONB 직렬화
        serializeJsonbFields(params);

        int updated = sqlSession.update("webTypeStandard.updateWebType", params);
        if (updated == 0) return null;

        String webType = (String) params.get("web_type");
        log.info("웹타입 수정: web_type={}", webType);

        Map<String, Object> result = sqlSession.selectOne(
                "webTypeStandard.selectWebType", Map.of("web_type", webType));
        if (result != null) parseJsonFields(result);
        return result;
    }

    // ── 삭제 ───────────────────────────────────────────────────────
    @Transactional
    public boolean deleteWebType(String webType) {
        int deleted = sqlSession.delete(
                "webTypeStandard.deleteWebType", Map.of("web_type", webType));
        if (deleted == 0) return false;
        log.info("웹타입 삭제: web_type={}", webType);
        return true;
    }

    // ── 정렬 순서 일괄 업데이트 ────────────────────────────────────
    @Transactional
    public void updateWebTypeSortOrder(Map<String, Object> params) {
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> webTypes = (List<Map<String, Object>>) params.get("web_types");
        String userId = (String) params.get("user_id");

        for (Map<String, Object> item : webTypes) {
            item.put("user_id", userId);
            sqlSession.update("webTypeStandard.updateSortOrder", item);
        }
        log.info("웹타입 정렬 순서 일괄 업데이트: {} 건", webTypes.size());
    }

    // ══ private helpers ═══════════════════════════════════════════

    /**
     * JSONB 컬럼(snake_case) → Java 객체 역직렬화 후 camelCase 키로 재매핑
     * resultType="map"은 map-underscore-to-camel-case 미적용 → snake_case로 반환
     */
    private void parseJsonFields(Map<String, Object> row) {
        for (Map.Entry<String, String> entry : JSONB_DB_TO_RESPONSE.entrySet()) {
            String dbKey = entry.getKey();
            String responseKey = entry.getValue();
            Object val = row.remove(dbKey);
            if (val instanceof String s && !s.isBlank()) {
                try {
                    row.put(responseKey, objectMapper.readValue(s, Object.class));
                } catch (Exception ignored) {
                    row.put(responseKey, val);
                }
            } else if (val != null) {
                row.put(responseKey, val);
            }
        }
    }

    /** JSONB 필드를 DB 저장 전 JSON 문자열로 직렬화 */
    private void serializeJsonbFields(Map<String, Object> params) {
        for (String field : JSONB_FIELDS) {
            Object val = params.get(field);
            if (val != null && !(val instanceof String)) {
                try {
                    params.put(field, objectMapper.writeValueAsString(val));
                } catch (JsonProcessingException e) {
                    log.warn("JSONB 직렬화 실패: field={}, error={}", field, e.getMessage());
                    params.put(field, "{}");
                }
            }
        }
    }
}
