// @vitest-environment jsdom

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import ReviewActions from '../ReviewActions';

// ── 모듈 모킹 ────────────────────────────────────────────────────────────────

vi.mock('@/contexts/AuthContext', () => ({
    useAuth: vi.fn(() => ({
        user: {
            getIdToken: vi.fn().mockResolvedValue('mock-token'),
            displayName: '테스트 검토자',
            email: 'reviewer@test.com',
        },
        userRole: 'reviewer',
        loading: false,
        customName: null,
        signInWithGoogle: vi.fn(),
        signOut: vi.fn(),
    })),
}));

vi.mock('sonner', () => ({
    toast: {
        success: vi.fn(),
        error: vi.fn(),
    },
}));

// ── global fetch 모킹 ─────────────────────────────────────────────────────
global.fetch = vi.fn();

// ── 헬퍼 ─────────────────────────────────────────────────────────────────────

function mockFetchSuccess() {
    (global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce(
        new Response(JSON.stringify({ success: true }), { status: 200 })
    );
}

function mockFetchFailure() {
    (global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce(
        new Response(JSON.stringify({ message: '서버 오류' }), { status: 500 })
    );
}

const defaultProps = {
    docId: 'test-doc-001',
};

// ── 테스트 ─────────────────────────────────────────────────────────────────────

describe('ReviewActions', () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });

    // ----------------------------------------------------------
    // 기본 렌더링
    // ----------------------------------------------------------
    describe('기본 렌더링', () => {
        it('승인 버튼이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            expect(screen.getByRole('button', { name: '승인' })).toBeDefined();
        });

        it('수정요청 버튼이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            expect(screen.getByRole('button', { name: '수정요청' })).toBeDefined();
        });

        it('거절 버튼이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            expect(screen.getByRole('button', { name: '거절' })).toBeDefined();
        });

        it('초기 상태에서 폼(textarea)은 표시되지 않는다', () => {
            render(<ReviewActions {...defaultProps} />);
            expect(screen.queryByRole('textbox')).toBeNull();
        });
    });

    // ----------------------------------------------------------
    // 버튼 클릭 → 폼 확장
    // ----------------------------------------------------------
    describe('버튼 클릭 시 폼 확장', () => {
        it('승인 버튼 클릭 시 코멘트 textarea가 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.getByPlaceholderText('검토 의견을 입력하세요...')).toBeDefined();
        });

        it('수정요청 버튼 클릭 시 코멘트 textarea가 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '수정요청' }));
            expect(screen.getByPlaceholderText('검토 의견을 입력하세요...')).toBeDefined();
        });

        it('거절 버튼 클릭 시 코멘트 textarea가 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '거절' }));
            expect(screen.getByPlaceholderText('검토 의견을 입력하세요...')).toBeDefined();
        });

        it('버튼 클릭 후 근거 유형 드롭다운이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.getByText('근거 유형 (선택)')).toBeDefined();
        });

        it('버튼 클릭 후 출처 추가 버튼이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.getByText('+ 추가')).toBeDefined();
        });

        it('버튼 클릭 후 제출 버튼이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.getByRole('button', { name: '제출' })).toBeDefined();
        });

        it('버튼 클릭 후 취소 버튼이 표시된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.getByRole('button', { name: '취소' })).toBeDefined();
        });

        it('같은 버튼을 다시 클릭하면 폼이 닫힌다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.getByPlaceholderText('검토 의견을 입력하세요...')).toBeDefined();
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            expect(screen.queryByRole('textbox')).toBeNull();
        });

        it('취소 버튼 클릭 시 폼이 닫힌다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '거절' }));
            fireEvent.click(screen.getByRole('button', { name: '취소' }));
            expect(screen.queryByRole('textbox')).toBeNull();
        });
    });

    // ----------------------------------------------------------
    // 출처 첨부 추가/삭제
    // ----------------------------------------------------------
    describe('출처 첨부 관리', () => {
        it('추가 버튼 클릭 시 URL/라벨 입력 필드가 추가된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByText('+ 추가'));
            expect(screen.getByPlaceholderText('URL')).toBeDefined();
            expect(screen.getByPlaceholderText('라벨')).toBeDefined();
        });

        it('삭제 버튼 클릭 시 해당 첨부가 제거된다', () => {
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByText('+ 추가'));
            expect(screen.getByPlaceholderText('URL')).toBeDefined();
            const deleteBtn = screen.getByLabelText('삭제');
            fireEvent.click(deleteBtn);
            expect(screen.queryByPlaceholderText('URL')).toBeNull();
        });
    });

    // ----------------------------------------------------------
    // 제출 유효성 검사
    // ----------------------------------------------------------
    describe('제출 유효성 검사', () => {
        it('수정요청 시 코멘트 없이 제출하면 toast.error가 호출된다', async () => {
            const { toast } = await import('sonner');
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '수정요청' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            expect(toast.error).toHaveBeenCalledWith('코멘트를 입력해주세요.');
        });

        it('거절 시 코멘트 없이 제출하면 toast.error가 호출된다', async () => {
            const { toast } = await import('sonner');
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '거절' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            expect(toast.error).toHaveBeenCalledWith('코멘트를 입력해주세요.');
        });

        it('승인 시 코멘트 없이도 제출 가능하다 (fetch 호출)', async () => {
            mockFetchSuccess();
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(global.fetch).toHaveBeenCalledOnce();
            });
        });
    });

    // ----------------------------------------------------------
    // 제출 성공/실패
    // ----------------------------------------------------------
    describe('제출 성공/실패', () => {
        it('승인 제출 성공 시 POST /api/wiki/entries/${docId}/review 를 호출한다', async () => {
            mockFetchSuccess();
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(global.fetch).toHaveBeenCalledOnce();
            });
            const [url, options] = (global.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
            expect(url).toBe(`/api/wiki/entries/${defaultProps.docId}/review`);
            expect(options.method).toBe('POST');
        });

        it('승인 제출 성공 시 body에 decision: "approve"가 포함된다', async () => {
            mockFetchSuccess();
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(global.fetch).toHaveBeenCalledOnce();
            });
            const [, options] = (global.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
            const body = JSON.parse(options.body);
            expect(body.decision).toBe('approve');
        });

        it('제출 성공 시 toast.success가 호출된다', async () => {
            const { toast } = await import('sonner');
            mockFetchSuccess();
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(toast.success).toHaveBeenCalled();
            });
        });

        it('제출 성공 시 onActionComplete 콜백이 호출된다', async () => {
            mockFetchSuccess();
            const onActionComplete = vi.fn();
            render(<ReviewActions docId={defaultProps.docId} onActionComplete={onActionComplete} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(onActionComplete).toHaveBeenCalledOnce();
            });
        });

        it('제출 실패 시 toast.error가 호출된다', async () => {
            const { toast } = await import('sonner');
            mockFetchFailure();
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(toast.error).toHaveBeenCalled();
            });
        });

        it('Authorization 헤더에 Bearer 토큰이 포함된다', async () => {
            mockFetchSuccess();
            render(<ReviewActions {...defaultProps} />);
            fireEvent.click(screen.getByRole('button', { name: '승인' }));
            fireEvent.click(screen.getByRole('button', { name: '제출' }));
            await waitFor(() => {
                expect(global.fetch).toHaveBeenCalledOnce();
            });
            const [, options] = (global.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
            expect(options.headers['Authorization']).toBe('Bearer mock-token');
        });
    });
});
