import { describe, it, expect, vi, beforeEach } from 'vitest';

vi.mock('../../src/renderer/inbox/api.js', () => ({
  inboxApi: {
    // promotion IPC
    listPromotionCandidates: vi.fn(async () => []),
    getPromotionDismissedTags: vi.fn(async () => []),
    addPromotionDismissedTag: vi.fn(async () => undefined),
    getPromotionSnoozeUntil: vi.fn(async () => 0),
    setPromotionSnoozeUntil: vi.fn(async () => undefined),
    // refreshMeta deps
    getContinuity: vi.fn(async () => ({ weekStart: '', weekCount: 0, weekTarget: 7, consecutiveCompleteWeeks: 0, showRecoveryToast: false, lastNoteAt: null })),
    getPendingCount: vi.fn(async () => 0),
    getOllamaStatus: vi.fn(async () => ({ ok: true })),
    getTodayCount: vi.fn(async () => 0),
    getTrashCount: vi.fn(async () => 0),
    listExpired: vi.fn(async () => []),
    getFailedCount: vi.fn(async () => 0),
    listRecallCandidate: vi.fn(async () => null),
    countsByStatus: vi.fn(async () => ({ active: 0, completed: 0, archived: 0, trashed: 0 })),
    getSettings: vi.fn(async () => ({ ai_enabled: true }))
  },
  notebookApi: {
    list: vi.fn(async () => []),
    create: vi.fn(async (i: { name: string; color?: string }) => ({
      ok: true as const,
      notebook: { id: 'nb-promo', name: i.name, color: i.color ?? null, createdAt: 't', updatedAt: 't', noteCount: 0 }
    })),
    moveNote: vi.fn(async () => ({ ok: true as const })),
    rename: vi.fn(async () => ({ ok: true as const })),
    setColor: vi.fn(async () => ({ ok: true as const })),
    delete: vi.fn(async () => ({ ok: true as const }))
  }
}));

import { useInbox } from '../../src/renderer/inbox/store.js';
import { inboxApi } from '../../src/renderer/inbox/api.js';
import { notebookApi } from '../../src/renderer/inbox/api.js';

type MockInboxApi = {
  listPromotionCandidates: ReturnType<typeof vi.fn>;
  getPromotionDismissedTags: ReturnType<typeof vi.fn>;
  addPromotionDismissedTag: ReturnType<typeof vi.fn>;
  getPromotionSnoozeUntil: ReturnType<typeof vi.fn>;
  setPromotionSnoozeUntil: ReturnType<typeof vi.fn>;
};
type MockNotebookApi = {
  create: ReturnType<typeof vi.fn>;
  moveNote: ReturnType<typeof vi.fn>;
};

const mockInbox = inboxApi as unknown as MockInboxApi;
const mockNotebook = notebookApi as unknown as MockNotebookApi;

describe('store promotion actions', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    useInbox.setState({ promotionCandidates: [], notebooks: [], sidebarVisible: false, selectedNotebookId: null } as never);
  });

  it('loadPromotionCandidates: 후보 목록 반환 + suggestedName toTitleCase 변환', async () => {
    mockInbox.getPromotionDismissedTags.mockResolvedValueOnce([]);
    mockInbox.getPromotionSnoozeUntil.mockResolvedValueOnce(0);
    mockInbox.listPromotionCandidates.mockResolvedValueOnce([
      { tag: 'machine-learning', noteIds: ['n1', 'n2', 'n3'], suggestedName: '' }
    ]);
    await useInbox.getState().loadPromotionCandidates();
    const candidates = useInbox.getState().promotionCandidates;
    expect(candidates).toHaveLength(1);
    expect(candidates[0]!.suggestedName).toBe('Machine Learning');
    expect(candidates[0]!.noteIds).toEqual(['n1', 'n2', 'n3']);
  });

  it('loadPromotionCandidates: snooze 유효 시 빈 배열', async () => {
    mockInbox.getPromotionDismissedTags.mockResolvedValueOnce([]);
    // 24h 후 만료되는 snooze
    mockInbox.getPromotionSnoozeUntil.mockResolvedValueOnce(Date.now() + 24 * 60 * 60 * 1000);
    mockInbox.listPromotionCandidates.mockResolvedValueOnce([
      { tag: 'work', noteIds: ['n1', 'n2', 'n3'], suggestedName: '' }
    ]);
    await useInbox.getState().loadPromotionCandidates();
    expect(useInbox.getState().promotionCandidates).toHaveLength(0);
  });

  it('loadPromotionCandidates: dismissed tag 는 제외', async () => {
    mockInbox.getPromotionDismissedTags.mockResolvedValueOnce(['work']);
    mockInbox.getPromotionSnoozeUntil.mockResolvedValueOnce(0);
    mockInbox.listPromotionCandidates.mockResolvedValueOnce([
      { tag: 'work', noteIds: ['n1', 'n2', 'n3'], suggestedName: '' },
      { tag: 'study', noteIds: ['n4', 'n5', 'n6'], suggestedName: '' }
    ]);
    await useInbox.getState().loadPromotionCandidates();
    const candidates = useInbox.getState().promotionCandidates;
    expect(candidates).toHaveLength(1);
    expect(candidates[0]!.tag).toBe('study');
  });

  it('dismissPromotion: addPromotionDismissedTag 호출 + state 에서 그 tag 제거', async () => {
    useInbox.setState({
      promotionCandidates: [
        { tag: 'work', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Work' },
        { tag: 'study', noteIds: ['n4', 'n5'], suggestedName: 'Study' }
      ]
    } as never);
    await useInbox.getState().dismissPromotion('work');
    expect(mockInbox.addPromotionDismissedTag).toHaveBeenCalledWith('work');
    const candidates = useInbox.getState().promotionCandidates;
    expect(candidates).toHaveLength(1);
    expect(candidates[0]!.tag).toBe('study');
  });

  it('snoozePromotion: setPromotionSnoozeUntil 24h 후로 호출 + state 비우기', async () => {
    useInbox.setState({
      promotionCandidates: [
        { tag: 'work', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Work' }
      ]
    } as never);
    const before = Date.now();
    await useInbox.getState().snoozePromotion();
    const [[ms]] = mockInbox.setPromotionSnoozeUntil.mock.calls as [[number]];
    expect(ms).toBeGreaterThan(before + 23 * 60 * 60 * 1000);
    expect(ms).toBeLessThan(before + 25 * 60 * 60 * 1000);
    expect(useInbox.getState().promotionCandidates).toHaveLength(0);
  });

  it('acceptPromotion: notebook 생성 + moveNote 호출 + sidebar 열림 + selectedNotebookId 설정', async () => {
    useInbox.setState({
      promotionCandidates: [
        { tag: 'work', noteIds: ['n1', 'n2'], suggestedName: 'Work' }
      ],
      notebooks: []
    } as never);
    await useInbox.getState().acceptPromotion('work', 'Work', '#0a4b80');
    expect(mockNotebook.create).toHaveBeenCalledWith({ name: 'Work', color: '#0a4b80' });
    expect(mockNotebook.moveNote).toHaveBeenCalledTimes(2);
    expect(mockNotebook.moveNote).toHaveBeenCalledWith('n1', 'nb-promo');
    expect(mockNotebook.moveNote).toHaveBeenCalledWith('n2', 'nb-promo');
    const state = useInbox.getState();
    expect(state.sidebarVisible).toBe(true);
    expect(state.selectedNotebookId).toBe('nb-promo');
    expect(state.promotionCandidates.find((c) => c.tag === 'work')).toBeUndefined();
    expect(state.notebooks.some((n) => n.id === 'nb-promo')).toBe(true);
  });
});
