package com.goldenchart.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.goldenchart.entity.GcChartSlot;
import com.goldenchart.entity.GcChartWorkspace;
import com.goldenchart.repository.GcChartWorkspaceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 차트 워크스페이스·슬롯 설정 저장/조회 서비스.
 * userId 또는 deviceId 중 하나를 식별자로 사용한다.
 */
@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class ChartSettingsService {

    private final GcChartWorkspaceRepository workspaceRepo;
    private final ObjectMapper objectMapper;

    // ── 워크스페이스 조회 ──────────────────────────────────────────────────────

    @Transactional(readOnly = true)
    public GcChartWorkspace getWorkspace(Long userId, String deviceId) {
        return findWorkspace(userId, deviceId).orElse(null);
    }

    @Transactional(readOnly = true)
    public GcChartWorkspace getWorkspaceWithSlots(Long userId, String deviceId) {
        if (userId != null) {
            return workspaceRepo.findWithSlotsByUserId(userId).orElse(null);
        }
        return workspaceRepo.findWithSlotsByDeviceId(deviceId).orElse(null);
    }

    // ── 워크스페이스 저장 (생성 또는 전체 업데이트) ────────────────────────────

    public GcChartWorkspace saveWorkspace(Long userId, String deviceId,
                                          String layoutId, String syncOptionsJson) {
        GcChartWorkspace ws = findWorkspace(userId, deviceId)
            .orElseGet(() -> GcChartWorkspace.builder()
                .userId(userId)
                .deviceId(deviceId)
                .layoutId(layoutId)
                .slots(new ArrayList<>())
                .build());
        ws.setLayoutId(layoutId);
        ws.setSyncOptionsJson(syncOptionsJson);
        return workspaceRepo.save(ws);
    }

    // ── 슬롯 설정 저장 ─────────────────────────────────────────────────────────

    public GcChartSlot saveSlot(Long userId, String deviceId, int slotIndex,
                                 Map<String, Object> slotData) {
        GcChartWorkspace ws = findOrCreateWorkspace(userId, deviceId);

        GcChartSlot slot = ws.getSlots().stream()
            .filter(s -> s.getSlotIndex() == slotIndex)
            .findFirst()
            .orElseGet(() -> {
                GcChartSlot newSlot = GcChartSlot.builder()
                    .workspace(ws)
                    .slotIndex(slotIndex)
                    .build();
                ws.getSlots().add(newSlot);
                return newSlot;
            });

        applySlotData(slot, slotData);
        workspaceRepo.save(ws);
        return slot;
    }

    // ── 전체 워크스페이스 + 슬롯 일괄 저장 (프론트 최초 로드 또는 전체 저장) ──

    public GcChartWorkspace saveAll(Long userId, String deviceId,
                                    String layoutId, String syncOptionsJson,
                                    List<Map<String, Object>> slotsData) {
        GcChartWorkspace ws = findOrCreateWorkspace(userId, deviceId);
        ws.setLayoutId(layoutId);
        ws.setSyncOptionsJson(syncOptionsJson);

        // 슬롯 동기화
        List<GcChartSlot> existing = ws.getSlots();
        for (int i = 0; i < slotsData.size(); i++) {
            final int idx = i;
            GcChartSlot slot = existing.stream()
                .filter(s -> s.getSlotIndex() == idx)
                .findFirst()
                .orElseGet(() -> {
                    GcChartSlot ns = GcChartSlot.builder()
                        .workspace(ws).slotIndex(idx).build();
                    existing.add(ns);
                    return ns;
                });
            applySlotData(slot, slotsData.get(i));
        }
        // 초과 슬롯 제거
        existing.removeIf(s -> s.getSlotIndex() >= slotsData.size());

        return workspaceRepo.save(ws);
    }

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

    private java.util.Optional<GcChartWorkspace> findWorkspace(Long userId, String deviceId) {
        if (userId != null) return workspaceRepo.findByUserId(userId);
        return workspaceRepo.findByDeviceId(deviceId);
    }

    private GcChartWorkspace findOrCreateWorkspace(Long userId, String deviceId) {
        return findWorkspace(userId, deviceId)
            .orElseGet(() -> workspaceRepo.save(GcChartWorkspace.builder()
                .userId(userId).deviceId(deviceId).layoutId("1").build()));
    }

    private void applySlotData(GcChartSlot slot, Map<String, Object> d) {
        if (d.containsKey("symbol"))         slot.setSymbol((String) d.get("symbol"));
        if (d.containsKey("timeframe"))      slot.setTimeframe((String) d.get("timeframe"));
        if (d.containsKey("chartType"))      slot.setChartType((String) d.get("chartType"));
        if (d.containsKey("theme"))          slot.setTheme((String) d.get("theme"));
        if (d.containsKey("mode"))           slot.setMode((String) d.get("mode"));
        if (d.containsKey("logScale"))       slot.setLogScale(Boolean.parseBoolean(d.get("logScale").toString()));
        if (d.containsKey("drawingsLocked")) slot.setDrawingsLocked(Boolean.parseBoolean(d.get("drawingsLocked").toString()));
        if (d.containsKey("drawingsVisible"))slot.setDrawingsVisible(Boolean.parseBoolean(d.get("drawingsVisible").toString()));
        try {
            if (d.containsKey("indicators"))     slot.setIndicatorsJson(objectMapper.writeValueAsString(d.get("indicators")));
            if (d.containsKey("drawings"))       slot.setDrawingsJson(objectMapper.writeValueAsString(d.get("drawings")));
            if (d.containsKey("paneLayout"))     slot.setPaneLayoutJson(objectMapper.writeValueAsString(d.get("paneLayout")));
            if (d.containsKey("mainChartStyle")) slot.setMainChartStyleJson(objectMapper.writeValueAsString(d.get("mainChartStyle")));
        } catch (JsonProcessingException e) {
            log.error("슬롯 JSON 직렬화 실패", e);
        }
    }
}
