package com.zioinfo.kintex.livenotice;

import com.zioinfo.kintex.auth.KintexPrincipal;
import com.zioinfo.kintex.common.audit.AuditLogService;
import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.livenotice.dto.LiveNoticeDtos.LiveNoticeCreateRequest;
import com.zioinfo.kintex.livenotice.dto.LiveNoticeDtos.LiveNoticeDto;
import com.zioinfo.kintex.work.notification.NotificationService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * F-B6 라이브 공지 서비스 단위테스트 — 검증·발행·§5B 알림 팬아웃·감사·보관.
 */
class LiveNoticeServiceTest {

    private LiveNoticeMapper mapper;
    private NotificationService notifications;
    private AuditLogService audit;
    private LiveNoticeService service;

    private final KintexPrincipal organizer =
            new KintexPrincipal("u1", "주최자", Map.of(), false);

    @BeforeEach
    void setUp() {
        mapper = mock(LiveNoticeMapper.class);
        notifications = mock(NotificationService.class);
        audit = mock(AuditLogService.class);
        service = new LiveNoticeService(mapper, notifications, audit);
        when(mapper.eventExists("e1")).thenReturn(1);
    }

    private Map<String, Object> row(String id) {
        return new java.util.HashMap<>(Map.of(
                "id", id, "eventId", "e1", "category", "URGENT", "title", "제목",
                "body", "본문", "pinned", true, "status", "published",
                "authorName", "주최자", "createdAt", "2026-07-23T00:00:00Z",
                "updatedAt", "2026-07-23T00:00:00Z"));
    }

    @Test
    void publish_rejectsUnknownCategory() {
        ApiException ex = assertThrows(ApiException.class, () -> service.publish(organizer, "e1",
                new LiveNoticeCreateRequest("WEIRD", "제목", "본문", false)));
        assertEquals(ErrorCode.VALIDATION, ex.getCode());
        verify(mapper, never()).insert(any());
    }

    @Test
    void publish_rejectsBlankTitle() {
        assertThrows(ApiException.class, () -> service.publish(organizer, "e1",
                new LiveNoticeCreateRequest("URGENT", "  ", "본문", false)));
    }

    @Test
    void publish_rejectsUnknownEvent() {
        when(mapper.eventExists("e1")).thenReturn(0);
        ApiException ex = assertThrows(ApiException.class, () -> service.publish(organizer, "e1",
                new LiveNoticeCreateRequest("URGENT", "제목", "본문", true)));
        assertEquals(ErrorCode.NOT_FOUND, ex.getCode());
    }

    @Test
    void publish_insertsFansOutAndAudits() {
        when(mapper.findEventMemberUserIds("e1")).thenReturn(List.of(
                Map.of("userId", "m1"), Map.of("userId", "m2")));
        when(mapper.findById(anyString())).thenReturn(row("ln-x"));

        LiveNoticeDto dto = service.publish(organizer, "e1",
                new LiveNoticeCreateRequest("urgent", "긴급 공지", "내용", true));

        assertEquals("URGENT", dto.category());
        verify(mapper).insert(any());
        // §5B 알림 모듈로 멤버 2명 팬아웃(중복 인프라 없이 재사용).
        verify(notifications, times(2)).notify(anyString(), eq("e1"), eq("SYSTEM"),
                anyString(), anyString(), anyString());
        verify(audit).record(eq("u1"), anyString(), eq("LIVE_NOTICE_PUBLISH"),
                eq("event_live_notice"), anyString(), eq("e1"), anyString(), eq("SUCCESS"));
    }

    @Test
    void publish_survivesFanoutFailure() {
        when(mapper.findEventMemberUserIds("e1")).thenThrow(new RuntimeException("db down"));
        when(mapper.findById(anyString())).thenReturn(row("ln-x"));
        // 팬아웃 실패해도 발행은 성공(공개 피드가 정본).
        assertNotNull(service.publish(organizer, "e1",
                new LiveNoticeCreateRequest("GENERAL", "안내", "내용", false)));
        verify(mapper).insert(any());
    }

    @Test
    void archive_notFoundOnEventMismatch() {
        when(mapper.findById("ln-x")).thenReturn(Map.of("id", "ln-x", "eventId", "OTHER"));
        assertThrows(ApiException.class, () -> service.archive(organizer, "e1", "ln-x"));
        verify(mapper, never()).archive(anyString(), anyString());
    }

    @Test
    void publicFeed_mapsRows() {
        when(mapper.findPublicFeed("e1", 100)).thenReturn(List.of(row("ln-1"), row("ln-2")));
        List<LiveNoticeDto> feed = service.publicFeed("e1", 0);
        assertEquals(2, feed.size());
        assertTrue(feed.get(0).pinned());
    }
}
