import { describe, expect, it } from 'vitest';
import { mergeMobileBean, MockGitBackend, syncWithGit, type GitConfig } from '../src/services/sync/gitSync';
import { WebDAVClient, MockFetch, syncWithWebDAV, backupToWebDAV, type WebDAVConfig } from '../src/services/sync/webdavSync';
import { getMonthGrid } from '../src/domain/stats/calendarGrid';
import type { Transaction } from '../src/domain/core/types';

const tx = (over: Partial<Transaction>): Transaction => ({
  id: 't1', date: '2026-07-10', flag: '*', postings: [], tags: [], links: [], source: 'mobile.bean', raw: '',
  ...over,
});

// ============ gitSync ============

describe('gitSync', () => {
  const config: GitConfig = { remoteUrl: 'https://github.com/x/y.git', branch: 'main', autoSync: true, syncIntervalMin: 30 };

  it('内容相同时 noop', async () => {
    const backend = new MockGitBackend('same-content');
    const result = await syncWithGit(backend, config, 'same-content');
    expect(result.action).toBe('noop');
  });

  it('本地有新增内容时 pushed', async () => {
    const backend = new MockGitBackend('remote-content');
    const result = await syncWithGit(backend, config, 'remote-content\n2026-07-13 * "新增"');
    expect(result.action).toBe('pushed');
    expect(backend.pushed).toBe(true);
  });

  it('mergeMobileBean union 合并', () => {
    const local = '2026-07-01 * "a"\n  A 1 CNY\n  B -1 CNY';
    const remote = '2026-07-02 * "b"\n  C 2 CNY\n  D -2 CNY';
    const result = mergeMobileBean(local, remote);
    expect(result.content).toContain('"a"');
    expect(result.content).toContain('"b"');
    expect(result.conflicts).toHaveLength(0);
  });

  it('mergeMobileBean 相同内容无变化', () => {
    const content = 'same';
    expect(mergeMobileBean(content, content).content).toBe('same');
  });
});

// ============ webdavSync ============

describe('webdavSync', () => {
  const config: WebDAVConfig = {
    url: 'https://dav.example.com/beancount/',
    username: 'user', password: 'pass',
    remotePath: '/mobile.bean', backupKeepCount: 3,
  };

  it('首次（远程无文件）→ pushed', async () => {
    const mockFetch = new MockFetch();
    const client = new WebDAVClient(config, mockFetch.fetch);
    const result = await syncWithWebDAV(client, 'local-content', '2026-01-01');
    expect(result.action).toBe('pushed');
  });

  it('内容相同 → noop', async () => {
    const mockFetch = new MockFetch();
    mockFetch.setFile('/mobile.bean', 'same');
    const client = new WebDAVClient(config, mockFetch.fetch);
    const result = await syncWithWebDAV(client, 'same', '2026-01-01');
    expect(result.action).toBe('noop');
  });

  it('远程有更新 → conflict', async () => {
    const mockFetch = new MockFetch();
    mockFetch.setFile('/mobile.bean', 'remote-new', '2026-07-14T00:00:00');
    const client = new WebDAVClient(config, mockFetch.fetch);
    const result = await syncWithWebDAV(client, 'local-new', '2026-01-01');
    expect(result.action).toBe('conflict');
    expect(result.conflict).toBeDefined();
  });

  it('仅本地变更 → pushed', async () => {
    const mockFetch = new MockFetch();
    mockFetch.setFile('/mobile.bean', 'old', '2020-01-01');
    const client = new WebDAVClient(config, mockFetch.fetch);
    const result = await syncWithWebDAV(client, 'new-local', '2026-07-14T00:00:00');
    expect(result.action).toBe('pushed');
  });

  it('backupToWebDAV 上传备份', async () => {
    const mockFetch = new MockFetch();
    const client = new WebDAVClient(config, mockFetch.fetch);
    const backupPath = await backupToWebDAV(client, 'backup-content', config);
    expect(backupPath).toContain('backup-');
  });

  it('WebDAVClient stat 获取 lastModified', async () => {
    const mockFetch = new MockFetch();
    mockFetch.setFile('/mobile.bean', 'content', '2026-07-13');
    const client = new WebDAVClient(config, mockFetch.fetch);
    const info = await client.stat();
    expect(info.exists).toBe(true);
  });

  it('WebDAVClient get 下载', async () => {
    const mockFetch = new MockFetch();
    mockFetch.setFile('/mobile.bean', 'downloaded');
    const client = new WebDAVClient(config, mockFetch.fetch);
    const content = await client.get();
    expect(content).toBe('downloaded');
  });
});

// ============ CalendarView getMonthGrid ============

describe('CalendarView getMonthGrid', () => {
  const transactions: Transaction[] = [
    tx({ id: 't1', date: '2026-07-05', postings: [{ account: 'Assets:A', amount: '-10', currency: 'CNY' }, { account: 'Expenses:Food', amount: '10', currency: 'CNY' }] }),
    tx({ id: 't2', date: '2026-07-10', postings: [{ account: 'Assets:A', amount: '100', currency: 'CNY' }, { account: 'Income:Salary', amount: '-100', currency: 'CNY' }] }),
    tx({ id: 't3', date: '2026-07-15', postings: [{ account: 'Assets:A', amount: '-5', currency: 'CNY' }, { account: 'Expenses:Food', amount: '5', currency: 'CNY' }] }),
  ];

  it('2026年7月日历网格', () => {
    const weeks = getMonthGrid(2026, 7, transactions);
    // 至少 5 周
    expect(weeks.length).toBeGreaterThanOrEqual(5);
    // 每周 7 天
    for (const week of weeks) {
      expect(week).toHaveLength(7);
    }
  });

  it('有交易的日期标记 income/expense', () => {
    const weeks = getMonthGrid(2026, 7, transactions);
    const allCells = weeks.flat();
    const day5 = allCells.find(c => c.day === 5 && c.isCurrentMonth);
    expect(day5?.hasExpense).toBe(true);
    expect(day5?.count).toBe(1);
    const day10 = allCells.find(c => c.day === 10 && c.isCurrentMonth);
    expect(day10?.hasIncome).toBe(true);
  });

  it('日期格式为 YYYY-MM-DD', () => {
    const weeks = getMonthGrid(2026, 7, transactions);
    const cell = weeks.flat().find(c => c.day === 1 && c.isCurrentMonth);
    expect(cell?.date).toBe('2026-07-01');
  });

  it('上下月填充标记 isCurrentMonth=false', () => {
    const weeks = getMonthGrid(2026, 7, transactions);
    const firstCell = weeks[0][0];
    // 2026-07-01 是周三，前 3 天是 6 月
    expect(firstCell.isCurrentMonth).toBe(false);
  });
});
