package com.zioinfo.kintex.rules;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * 규정 룰 엔진 단위테스트 — 실제 배포 룰셋(compliance-v1.json)을 로드해 평가 정확도를 검증한다.
 * 대상: 높이 5m 차단·통로폭·비상구·부스 겹침·홀별 바닥하중(lteHall)·방염·리깅(warn) + submittable 집계.
 */
class ComplianceRuleEngineTest {

    private ComplianceRuleEngine engine;

    @BeforeEach
    void setUp() {
        RuleProperties props = new RuleProperties();
        props.setComplianceRuleset(new ClassPathResource("rulesets/compliance-v1.json"));
        props.setRateRuleset(new ClassPathResource("rulesets/rates-v1.json"));
        RuleSetLoader loader = new RuleSetLoader(props, new ObjectMapper());
        loader.load();
        engine = new ComplianceRuleEngine(loader);
    }

    private Violation find(ComplianceReport r, String code) {
        return r.violations().stream().filter(v -> code.equals(v.code())).findFirst().orElse(null);
    }

    @Test
    void loadsRealRuleset() {
        ComplianceReport r = engine.evaluate("M2", Map.of("booth.height_m", 3.0), null);
        assertEquals("compliance-v1.1", r.rulesetVersion());
        assertNotNull(r.disclaimer());
    }

    @Test
    void m2_allWithinLimits_isSubmittable() {
        Map<String, Object> m = new HashMap<>();
        m.put("booth.height_m", 3.0);
        m.put("layout.min_aisle_width_m", 3.5);
        m.put("layout.exits_blocked_count", 0);
        m.put("layout.overlap_count", 0);
        m.put("booth.floor_load_t_per_m2", 4.0);
        ComplianceReport r = engine.evaluate("M2", m, "H7");
        assertEquals(0, r.blockCount());
        assertTrue(r.submittable());
        assertTrue(r.passCount() >= 5);
    }

    @Test
    void m2_heightOverFiveMeters_blocks() {
        ComplianceReport r = engine.evaluate("M2", Map.of("booth.height_m", 5.4), "H7");
        Violation v = find(r, "HEIGHT_MAX");
        assertNotNull(v);
        assertEquals("block", v.severity());
        assertFalse(r.submittable());
    }

    @Test
    void m2_aisleNarrowerThanThreeMeters_blocks() {
        ComplianceReport r = engine.evaluate("M2", Map.of("layout.min_aisle_width_m", 2.5), null);
        assertNotNull(find(r, "AISLE_WIDTH_MIN"));
        assertFalse(r.submittable());
    }

    @Test
    void m2_overlappingBooths_blocks_andReportsCount() {
        ComplianceReport r = engine.evaluate("M2", Map.of("layout.overlap_count", 2), null);
        Violation v = find(r, "BOOTH_OVERLAP");
        assertNotNull(v);
        assertEquals("block", v.severity());
        assertTrue(v.measured().contains("2"));
        assertFalse(r.submittable());
    }

    @Test
    void m2_floorLoad_hallSpecificLimit() {
        // 홀6 상한 2.0 t/㎡ — 3.0 은 초과(차단).
        ComplianceReport h6 = engine.evaluate("M2", Map.of("booth.floor_load_t_per_m2", 3.0), "H6");
        assertNotNull(find(h6, "FLOOR_LOAD"));
        // 홀7 상한 5.0 t/㎡ — 동일 3.0 은 통과.
        ComplianceReport h7 = engine.evaluate("M2", Map.of("booth.floor_load_t_per_m2", 3.0), "H7");
        assertEquals(null, find(h7, "FLOOR_LOAD"));
    }

    @Test
    void warnViolation_doesNotBlockSubmission() {
        // 인접 벽 이격 0.1m (< 0.3m) → CLEARANCE_WALL warn. warn 만이면 제출 가능(submittable true).
        ComplianceReport r = engine.evaluate("M2", Map.of("booth.clearance_wall_m", 0.1), null);
        Violation v = find(r, "CLEARANCE_WALL");
        assertNotNull(v);
        assertEquals("warn", v.severity());
        assertEquals(0, r.blockCount());
        assertEquals(1, r.warnCount());
        assertTrue(r.submittable());
    }

    @Test
    void riggingBetweenOperator_inRangeCurrentlyPasses() {
        // 현행 룰셋: RIGGING_RANGE operator=between → 6.5~8.5m 내이면 "통과"로 집계(위반 미발생).
        // 라벨 의도("구간 진입 시 D-7 필요")와 반대 — 갭 기록: _workspace/08_m2m5_contract_changes.md.
        ComplianceReport r = engine.evaluate("M3", Map.of("rigging.height_m", 7.0), null);
        assertEquals(null, find(r, "RIGGING_RANGE"));
        assertTrue(r.passCount() >= 1);
    }

    @Test
    void m3_nonFireRetardantMaterials_blocks() {
        ComplianceReport r = engine.evaluate("M3", Map.of("materials.all_fire_retardant", false), null);
        assertNotNull(find(r, "FIRE_RETARDANT"));
        assertFalse(r.submittable());
    }

    @Test
    void unmeasuredMetrics_areSkipped_notCountedAsPass() {
        ComplianceReport r = engine.evaluate("M2", Map.of(), null);
        assertEquals(0, r.blockCount());
        assertEquals(0, r.warnCount());
        assertEquals(0, r.passCount());
        assertTrue(r.violations().isEmpty());
    }

    @Test
    void unloadedRuleset_degradesGracefully() {
        RuleProperties props = new RuleProperties();
        props.setComplianceRuleset(new ClassPathResource("rulesets/does-not-exist.json"));
        RuleSetLoader loader = new RuleSetLoader(props, new ObjectMapper());
        loader.load();
        ComplianceReport r = new ComplianceRuleEngine(loader)
                .evaluate("M2", Map.of("booth.height_m", 9.9), null);
        assertEquals("compliance-unloaded", r.rulesetVersion());
        assertTrue(r.submittable());
        assertEquals(List.of(), r.violations());
    }
}
