package com.erp.service;

import com.erp.common.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ScheduleService extends BaseService {

    @Autowired
    private CommonService commonService;

    private static final String NS = "schedule.";

    public List<Map<String, Object>> getScheduleList(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        return sqlSession.selectList(NS + "getScheduleList", params);
    }

    public Map<String, Object> getScheduleInfo(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        return sqlSession.selectOne(NS + "getScheduleInfo", params);
    }

    /**
     * 스케줄 미리보기 (DB 저장 없음, 순수 계산)
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> previewSchedule(Map<String, Object> params) {
        Map<String, Object> config = (Map<String, Object>) params.get("config");
        List<Map<String, Object>> sourceData = (List<Map<String, Object>>) params.get("source_data");
        Map<String, Object> period = (Map<String, Object>) params.get("period");
        String companyCode = (String) params.get("company_code");

        if (config == null || config.get("schedule_type") == null) {
            throw new IllegalArgumentException("스케줄 설정(config)이 필요합니다.");
        }
        if (sourceData == null || sourceData.isEmpty()) {
            throw new IllegalArgumentException("소스 데이터가 필요합니다.");
        }

        if (period == null) {
            period = new HashMap<>();
            LocalDate now = LocalDate.now();
            period.put("start", now.withDayOfMonth(1).toString());
            period.put("end", now.withDayOfMonth(now.lengthOfMonth()).toString());
        }

        String scheduleType = (String) config.get("schedule_type");
        Map<String, Object> source = (Map<String, Object>) config.get("source");
        Map<String, Object> resource = (Map<String, Object>) config.get("resource");
        Map<String, Object> rules = config.containsKey("rules") ? (Map<String, Object>) config.get("rules") : new HashMap<>();

        String quantityField = source != null ? (String) source.get("quantity_field") : null;
        String dueDateField = source != null ? (String) source.get("due_date_field") : null;
        String resourceIdField = resource != null ? (String) resource.get("id_field") : null;
        String resourceNameField = resource != null ? (String) resource.get("name_field") : null;
        String resourceType = resource != null ? (String) resource.get("type") : null;
        int leadTimeDays = rules.containsKey("lead_time_days") ? ((Number) rules.get("lead_time_days")).intValue() : 3;
        String periodEnd = (String) period.get("end");

        // 소스 데이터를 리소스별(+기준일)로 그룹화
        Map<String, List<Map<String, Object>>> grouped = new LinkedHashMap<>();
        for (Map<String, Object> item : sourceData) {
            String resId = resourceIdField != null ? String.valueOf(item.getOrDefault(resourceIdField, "")) : "";
            if (resId.isEmpty()) continue;
            String groupKey = resId;
            if (dueDateField != null && item.get(dueDateField) != null) {
                String dueDate = item.get(dueDateField).toString();
                if (dueDate.length() >= 10) dueDate = dueDate.substring(0, 10);
                groupKey = resId + "|" + dueDate;
            }
            grouped.computeIfAbsent(groupKey, k -> new ArrayList<>()).add(item);
        }

        List<Map<String, Object>> toCreate = new ArrayList<>();
        double totalQty = 0;

        for (Map.Entry<String, List<Map<String, Object>>> entry : grouped.entrySet()) {
            String groupKey = entry.getKey();
            List<Map<String, Object>> items = entry.getValue();
            String[] parts = groupKey.split("\\|", 2);
            String resId = parts[0];
            String groupDueDate = parts.length > 1 ? parts[1] : null;
            String resName = resourceNameField != null
                    ? String.valueOf(items.get(0).getOrDefault(resourceNameField, resId)) : resId;

            double qty = 0;
            if (quantityField != null) {
                for (Map<String, Object> item : items) {
                    Object qtyVal = item.get(quantityField);
                    if (qtyVal != null) {
                        try { qty += Double.parseDouble(qtyVal.toString()); } catch (NumberFormatException ignored) {}
                    }
                }
            }
            if (qty <= 0) continue;

            String effectiveDueDate;
            if (groupDueDate != null) {
                effectiveDueDate = groupDueDate;
            } else if (dueDateField != null) {
                effectiveDueDate = items.stream()
                        .map(item -> item.get(dueDateField)).filter(Objects::nonNull)
                        .map(Object::toString).map(d -> d.length() >= 10 ? d.substring(0, 10) : d)
                        .min(String::compareTo).orElse(periodEnd);
            } else {
                effectiveDueDate = periodEnd;
            }

            LocalDate dueDateLocal = LocalDate.parse(effectiveDueDate);
            LocalDate startDateLocal = dueDateLocal.minusDays(leadTimeDays);
            String scheduleName = groupDueDate != null
                    ? resName + " (" + effectiveDueDate + ")"
                    : resName + " - " + scheduleType;

            Map<String, Object> schedule = new LinkedHashMap<>();
            schedule.put("schedule_type", scheduleType);
            schedule.put("schedule_name", scheduleName);
            schedule.put("resource_type", resourceType);
            schedule.put("resource_id", resId);
            schedule.put("resource_name", resName);
            schedule.put("start_date", startDateLocal.toString());
            schedule.put("end_date", effectiveDueDate);
            schedule.put("due_date", effectiveDueDate);
            schedule.put("plan_qty", qty);
            schedule.put("status", "PLANNED");
            schedule.put("source_table", source != null ? source.get("table_name") : null);
            schedule.put("source_group_key", resId);
            schedule.put("auto_generated", true);
            toCreate.add(schedule);
            totalQty += qty;
        }

        List<Map<String, Object>> toDelete = new ArrayList<>();
        if (!grouped.isEmpty()) {
            List<String> resourceIds = grouped.keySet().stream()
                    .map(k -> k.split("\\|")[0]).distinct().collect(Collectors.toList());
            Map<String, Object> existParams = new HashMap<>();
            existParams.put("company_code", companyCode);
            existParams.put("schedule_type", scheduleType);
            existParams.put("resource_ids", resourceIds);
            existParams.put("period_start", period.get("start"));
            existParams.put("period_end", period.get("end"));
            try {
                toDelete = sqlSession.selectList(NS + "getExistingScheduleList", existParams);
            } catch (Exception e) {
                log.warn("기존 스케줄 조회 실패: {}", e.getMessage());
            }
        }

        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("create_count", toCreate.size());
        summary.put("delete_count", toDelete.size());
        summary.put("update_count", 0);
        summary.put("total_qty", totalQty);

        Map<String, Object> preview = new LinkedHashMap<>();
        preview.put("to_create", toCreate);
        preview.put("to_delete", toDelete);
        preview.put("to_update", Collections.emptyList());
        preview.put("summary", summary);
        return preview;
    }

    /**
     * 스케줄 적용 (DB 저장)
     */
    @SuppressWarnings("unchecked")
    @Transactional
    public Map<String, Object> applySchedules(Map<String, Object> params) {
        Map<String, Object> preview = (Map<String, Object>) params.get("preview");
        Map<String, Object> options = (Map<String, Object>) params.get("options");
        String companyCode = (String) params.get("company_code");
        String userId = params.containsKey("user_id") ? (String) params.get("user_id") : "system";

        if (preview == null) {
            throw new IllegalArgumentException("미리보기(preview)가 필요합니다.");
        }

        List<Map<String, Object>> toDelete = (List<Map<String, Object>>) preview.getOrDefault("to_delete", Collections.emptyList());
        List<Map<String, Object>> toCreate = (List<Map<String, Object>>) preview.getOrDefault("to_create", Collections.emptyList());
        boolean deleteExisting = options == null || !Boolean.FALSE.equals(options.get("delete_existing"));

        int deleted = 0;
        if (deleteExisting && !toDelete.isEmpty()) {
            List<Integer> deleteIds = toDelete.stream()
                    .map(s -> {
                        Object id = s.get("schedule_id");
                        return id != null ? ((Number) id).intValue() : null;
                    })
                    .filter(Objects::nonNull).collect(Collectors.toList());
            if (!deleteIds.isEmpty()) {
                Map<String, Object> deleteParams = new HashMap<>();
                deleteParams.put("company_code", companyCode);
                deleteParams.put("id_list", deleteIds);
                sqlSession.delete(NS + "deleteSchedulesByIdList", deleteParams);
                deleted = deleteIds.size();
            }
        }

        int created = 0;
        for (Map<String, Object> schedule : toCreate) {
            Map<String, Object> insertParams = new HashMap<>(schedule);
            insertParams.put("company_code", companyCode);
            insertParams.put("generated_by", userId);
            insertParams.put("created_by", userId);
            insertParams.put("updated_by", userId);
            sqlSession.insert(NS + "insertSchedule", insertParams);
            created++;
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("created", created);
        result.put("deleted", deleted);
        result.put("updated", 0);
        return result;
    }

    @Transactional
    public int insertSchedule(Map<String, Object> params) {
        return sqlSession.insert(NS + "insertSchedule", params);
    }

    @Transactional
    public int updateSchedule(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        return sqlSession.update(NS + "updateSchedule", params);
    }

    @Transactional
    public Map<String, Object> deleteSchedule(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        int affected = sqlSession.delete(NS + "deleteSchedule", params);
        if (affected == 0) {
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "스케줄을 찾을 수 없거나 권한이 없습니다.");
            return result;
        }
        try {
            Map<String, Object> historyParams = new HashMap<>();
            historyParams.put("company_code", params.get("company_code"));
            historyParams.put("schedule_id", params.get("schedule_id"));
            historyParams.put("action", "DELETE");
            historyParams.put("changed_by", params.getOrDefault("user_id", "system"));
            sqlSession.insert(NS + "insertScheduleHistory", historyParams);
        } catch (Exception e) {
            log.warn("스케줄 이력 저장 실패 (무시): {}", e.getMessage());
        }
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        return result;
    }
}
