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.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

    private final CommonService commonService;
    private final ObjectMapper objectMapper;

    // ─────────────────────────────────────────────────────────
    // 목록 조회
    // ─────────────────────────────────────────────────────────

    /**
     * 레이아웃 목록 조회 (페이지네이션 + 필터)
     *
     * @return { data: List, total: int, page: int, size: int, totalPages: int }
     */
    public Map<String, Object> getLayouts(Map<String, Object> params) {
        int page = toInt(params.getOrDefault("page", 1), 1);
        int size = toInt(params.getOrDefault("size", 20), 20);

        // includePublic: String "true"/"false" → Boolean
        Object ipRaw = params.get("include_public");
        boolean includePublic = ipRaw == null || "true".equalsIgnoreCase(ipRaw.toString());

        params.put("page", page);
        params.put("size", size);
        params.put("limit", size);
        params.put("offset", (page - 1) * size);
        params.put("include_public", includePublic);

        List<Map<String, Object>> data  = sqlSession.selectList("layout.selectLayouts", params);
        int total = sqlSession.selectOne("layout.countLayouts", params);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("data", data);
        result.put("total", total);
        result.put("page", page);
        result.put("size", size);
        result.put("total_pages", (int) Math.ceil((double) total / size));
        return result;
    }

    // ─────────────────────────────────────────────────────────
    // 단건 조회
    // ─────────────────────────────────────────────────────────

    public Map<String, Object> getLayoutById(String layoutCode, String companyCode) {
        Map<String, Object> params = new HashMap<>();
        params.put("layout_code", layoutCode);
        params.put("company_code", companyCode);
        return sqlSession.selectOne("layout.selectLayoutByCode", params);
    }

    // ─────────────────────────────────────────────────────────
    // 생성
    // ─────────────────────────────────────────────────────────

    @Transactional
    public Map<String, Object> createLayout(Map<String, Object> body,
                                            String companyCode,
                                            String userId) {
        String layoutType = (String) body.get("layout_type");
        String layoutCode = generateLayoutCode(layoutType, companyCode);

        Map<String, Object> params = new HashMap<>();
        params.put("layout_code",    layoutCode);
        params.put("company_code",   companyCode);
        params.put("user_id",        userId);
        params.put("layout_name",    body.get("layout_name"));
        params.put("layout_name_eng", body.get("layout_name_eng"));
        params.put("description",   body.get("description"));
        params.put("layout_type",    layoutType);
        params.put("category",      body.get("category"));
        params.put("icon_name",      body.get("icon_name"));
        params.put("default_size",   toJsonString(body.get("default_size")));
        params.put("layout_config",  toJsonString(body.get("layout_config")));
        params.put("zones_config",   toJsonString(body.get("zones_config")));
        params.put("is_public",      toBooleanYN(body.get("is_public")));

        sqlSession.insert("layout.insertLayout", params);

        return getLayoutById(layoutCode, companyCode);
    }

    // ─────────────────────────────────────────────────────────
    // 수정
    // ─────────────────────────────────────────────────────────

    @Transactional
    public Map<String, Object> updateLayout(String layoutCode,
                                            Map<String, Object> body,
                                            String companyCode,
                                            String userId) {
        // 소유권 확인 (is_public 무관, 자사 레이아웃만 수정 가능)
        Map<String, Object> checkParams = new HashMap<>();
        checkParams.put("layout_code",  layoutCode);
        checkParams.put("company_code", companyCode);
        if (sqlSession.selectOne("layout.selectLayoutByCodeAndCompany", checkParams) == null) {
            return null;  // Controller 에서 404 처리
        }

        Map<String, Object> params = new HashMap<>();
        params.put("layout_code",  layoutCode);
        params.put("company_code", companyCode);
        params.put("user_id",      userId);

        // 존재하는 필드만 params 에 추가
        copyIfPresent(body, params, "layout_name");
        copyIfPresent(body, params, "layout_name_eng");
        copyIfPresent(body, params, "description");
        copyIfPresent(body, params, "layout_type");
        copyIfPresent(body, params, "category");
        copyIfPresent(body, params, "icon_name");

        // JSON 필드는 직렬화 후 추가
        if (body.containsKey("default_size"))  params.put("default_size",  toJsonString(body.get("default_size")));
        if (body.containsKey("layout_config")) params.put("layout_config", toJsonString(body.get("layout_config")));
        if (body.containsKey("zones_config"))  params.put("zones_config",  toJsonString(body.get("zones_config")));

        // boolean → Y/N
        if (body.containsKey("is_public")) params.put("is_public", toBooleanYN(body.get("is_public")));

        sqlSession.update("layout.updateLayout", params);
        return getLayoutById(layoutCode, companyCode);
    }

    // ─────────────────────────────────────────────────────────
    // 삭제 (소프트)
    // ─────────────────────────────────────────────────────────

    @Transactional
    public void deleteLayout(String layoutCode, String companyCode, String userId) {
        Map<String, Object> checkParams = new HashMap<>();
        checkParams.put("layout_code",  layoutCode);
        checkParams.put("company_code", companyCode);
        if (sqlSession.selectOne("layout.selectLayoutByCodeAndCompany", checkParams) == null) {
            throw new IllegalArgumentException("레이아웃을 찾을 수 없거나 삭제 권한이 없습니다.");
        }

        Map<String, Object> params = new HashMap<>();
        params.put("layout_code",  layoutCode);
        params.put("company_code", companyCode);
        params.put("user_id",      userId);
        sqlSession.update("layout.softDeleteLayout", params);
    }

    // ─────────────────────────────────────────────────────────
    // 복제
    // ─────────────────────────────────────────────────────────

    @Transactional
    public Map<String, Object> duplicateLayout(String layoutCode,
                                               String newName,
                                               String companyCode,
                                               String userId) {
        Map<String, Object> original = getLayoutById(layoutCode, companyCode);
        if (original == null) {
            throw new IllegalArgumentException("복제할 레이아웃을 찾을 수 없습니다.");
        }

        // DB 결과는 snake_case → createLayout 이 요구하는 camelCase 로 변환
        Map<String, Object> body = new HashMap<>();
        body.put("layout_name",    newName);
        body.put("layout_type",    original.get("layout_type"));
        body.put("category",      original.get("category"));
        body.put("description",   original.get("description"));
        body.put("icon_name",      original.get("icon_name"));
        body.put("default_size",   original.get("default_size"));
        body.put("layout_config",  original.get("layout_config"));
        body.put("zones_config",   original.get("zones_config"));
        body.put("is_public",      "N");

        // layoutNameEng 에 "Copy" 접미사
        Object engName = original.get("layout_name_eng");
        if (engName != null) {
            body.put("layout_name_eng", engName + " Copy");
        }

        return createLayout(body, companyCode, userId);
    }

    // ─────────────────────────────────────────────────────────
    // 카테고리별 개수
    // ─────────────────────────────────────────────────────────

    public Map<String, Object> getLayoutCountsByCategory(String companyCode) {
        Map<String, Object> params = new HashMap<>();
        params.put("company_code", companyCode);

        List<Map<String, Object>> rows = sqlSession.selectList(
                "layout.selectCountsByCategory", params);

        Map<String, Object> result = new LinkedHashMap<>();
        for (Map<String, Object> row : rows) {
            String category = String.valueOf(row.get("category"));
            Object count    = row.get("count");
            result.put(category, count instanceof Number
                ? ((Number) count).intValue()
                : Integer.parseInt(String.valueOf(count)));
        }
        return result;
    }

    // ─────────────────────────────────────────────────────────
    // private helpers
    // ─────────────────────────────────────────────────────────

    /**
     * 레이아웃 코드 자동 생성: {LAYOUT_TYPE}_{COMPANY_CODE}_{NNN}
     * Node.js generateLayoutCode 동일 로직
     */
    private String generateLayoutCode(String layoutType, String companyCode) {
        String prefix = layoutType.toUpperCase() + "_" + companyCode;

        Map<String, Object> params = new HashMap<>();
        params.put("prefix", prefix + "%");
        List<Map<String, Object>> existing = sqlSession.selectList(
                "layout.selectCodesLike", params);

        Pattern numPattern = Pattern.compile("_(\\d+)$");
        int maxNumber = existing.stream()
            .map(row -> String.valueOf(row.get("layout_code")))
            .mapToInt(code -> {
                Matcher m = numPattern.matcher(code);
                return m.find() ? Integer.parseInt(m.group(1)) : 0;
            })
            .max()
            .orElse(0);

        return String.format("%s_%03d", prefix, maxNumber + 1);
    }

    /** Object → JSON 문자열 (null 허용) */
    private String toJsonString(Object obj) {
        if (obj == null) return null;
        if (obj instanceof String s) return s;
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.warn("JSON 직렬화 실패: {}", e.getMessage());
            return null;
        }
    }

    /** Boolean/String → "Y"/"N" */
    private String toBooleanYN(Object val) {
        if (val == null) return "N";
        if (val instanceof Boolean b) return b ? "Y" : "N";
        return "true".equalsIgnoreCase(val.toString()) ? "Y" : "N";
    }

    /** params 에 key 가 존재하면 dest 에 복사 */
    private void copyIfPresent(Map<String, Object> src, Map<String, Object> dest, String key) {
        if (src.containsKey(key)) dest.put(key, src.get(key));
    }

    /** Object → int (기본값 포함) */
    private int toInt(Object val, int defaultVal) {
        if (val == null) return defaultVal;
        try { return Integer.parseInt(val.toString()); }
        catch (NumberFormatException e) { return defaultVal; }
    }
}
