import assert from 'node:assert/strict';
import test from 'node:test';
import type { VersionWithContext } from './derive';
import type { XiaobaoVersionRisk } from './xiaobao-risk';
import {
  filterXiaobaoRiskWarnings,
  filterXiaobaoWarningVersions,
  formatRemainingWork,
  getRiskScoreTone,
  getXiaobaoWarningRiskCount,
  getXiaobaoWarningUnreadUpdateCount,
  getXiaobaoWarningUpdateSignature,
  isXiaobaoWarningUpdated,
  markXiaobaoWarningRead,
  shouldSkipXiaobaoRiskInsightRequestForReadState,
  sanitizeRiskInsight,
} from './xiaobao-warning-view';

function version(patch: Partial<VersionWithContext> = {}): VersionWithContext {
  return {
    id: 'ver-1',
    name: 'V1.0',
    status: 'developing',
    releaseDate: null,
    createdAt: '2026-06-29T00:00:00.000Z',
    productId: 'prod-1',
    productName: 'FTB',
    projectId: 'proj-1',
    projectName: 'Project',
    expectedReleaseDate: '2026-07-05T10:00:00.000Z',
    members: [{ name: 'Alice', role: 'frontend' }],
    ...patch,
  };
}

function risk(patch: Partial<XiaobaoVersionRisk> = {}): XiaobaoVersionRisk {
  return {
    versionId: 'ver-1',
    versionName: 'V1.0',
    productId: 'prod-1',
    productName: 'Product A',
    projectId: 'proj-1',
    projectName: 'Project A',
    riskScore: 68,
    riskLevel: 'attention',
    expectedReleaseDate: '2026-07-05',
    confidence: 80,
    confidenceLevel: 'high',
    delayDays: 0,
    remainingWorkHours: 8,
    reasons: [],
    silentRisks: [],
    signals: {
      unfinishedCount: 1,
      openBugCount: 0,
      criticalBugCount: 0,
      failedTestCount: 0,
      blockedCount: 0,
      silentRiskCount: 0,
    },
    currentSnapshot: {
      versionId: 'ver-1',
      date: '2026-06-30',
      riskScore: 68,
      riskLevel: 'attention',
      openBugCount: 0,
      criticalBugCount: 0,
      failedTestCount: 0,
      blockedCount: 0,
      silentRiskCount: 0,
      confidence: 80,
      createdAt: '2026-06-30T10:00:00.000Z',
    },
    trend: { direction: 'flat', delta: 0, summary: 'Risk is stable.', pattern: 'stable' },
    ...patch,
  } as XiaobaoVersionRisk;
}

test('filterXiaobaoWarningVersions lets managers see every unfinished version', () => {
  const result = filterXiaobaoWarningVersions(
    [
      version({ id: 'ver-1', status: 'developing', members: [{ name: 'Alice', role: 'frontend' }] }),
      version({ id: 'ver-2', status: 'planned', members: [{ name: 'Bob', role: 'testing' }] }),
      version({ id: 'ver-3', status: 'released', members: [{ name: 'Alice', role: 'frontend' }] }),
    ],
    { canManage: true, userName: 'Alice' },
  );

  assert.deepEqual(result.map((item) => item.id), ['ver-1', 'ver-2']);
});

test('filterXiaobaoWarningVersions excludes paused released closed versions and paused projects', () => {
  const versions = [
    version({ id: 'ver-1', status: 'paused', members: [{ name: 'Alice', role: 'frontend' }] }),
    version({ id: 'ver-2', status: 'developing', members: [{ name: 'Alice', role: 'frontend' }] }),
    version({ id: 'ver-3', status: 'released', members: [{ name: 'Alice', role: 'frontend' }] }),
    version({ id: 'ver-4', status: 'closed', members: [{ name: 'Alice', role: 'frontend' }] }),
    version({ id: 'ver-5', status: 'developing', projectStatus: 'paused', members: [{ name: 'Alice', role: 'frontend' }] }),
  ];

  assert.deepEqual(
    filterXiaobaoWarningVersions(versions, { canManage: true, userName: 'Alice' }).map((item) => item.id),
    ['ver-2'],
  );
  assert.deepEqual(
    filterXiaobaoWarningVersions(versions, { canManage: false, userName: 'Alice' }).map((item) => item.id),
    ['ver-2'],
  );
});

test('filterXiaobaoWarningVersions limits non-managers to versions where they are a member', () => {
  const result = filterXiaobaoWarningVersions(
    [
      version({ id: 'ver-1', status: 'developing', members: [{ name: 'Alice', role: 'frontend' }] }),
      version({ id: 'ver-2', status: 'developing', members: [{ name: 'Bob', role: 'testing' }] }),
      version({ id: 'ver-3', status: 'closed', members: [{ name: 'Alice', role: 'frontend' }] }),
    ],
    { canManage: false, userName: 'Alice' },
  );

  assert.deepEqual(result.map((item) => item.id), ['ver-1']);
});

test('filterXiaobaoRiskWarnings hides on_track risks and badge count follows visible risks', () => {
  const risks = [
    risk({ versionId: 'ver-1', riskLevel: 'on_track', riskScore: 10 }),
    risk({ versionId: 'ver-2', riskLevel: 'attention', riskScore: 38 }),
    risk({ versionId: 'ver-3', riskLevel: 'blocked', riskScore: 100 }),
  ];

  assert.deepEqual(filterXiaobaoRiskWarnings(risks).map((item) => item.versionId), ['ver-2', 'ver-3']);
  assert.equal(getXiaobaoWarningRiskCount(risks), 2);
});

test('filterXiaobaoRiskWarnings filters by product project and risk tier', () => {
  const risks = [
    risk({ versionId: 'ver-1', productId: 'prod-1', projectId: 'proj-1', riskLevel: 'attention', riskScore: 38 }),
    risk({ versionId: 'ver-2', productId: 'prod-1', projectId: 'proj-2', riskLevel: 'likely_delayed', riskScore: 88 }),
    risk({ versionId: 'ver-3', productId: 'prod-2', projectId: 'proj-3', riskLevel: 'blocked', riskScore: 100 }),
  ];

  assert.deepEqual(
    filterXiaobaoRiskWarnings(risks, { productId: 'prod-1' }).map((item) => item.versionId),
    ['ver-1', 'ver-2'],
  );
  assert.deepEqual(
    filterXiaobaoRiskWarnings(risks, { productId: 'prod-1', projectId: 'proj-2' }).map((item) => item.versionId),
    ['ver-2'],
  );
  assert.deepEqual(
    filterXiaobaoRiskWarnings(risks, { riskFilter: 'high' }).map((item) => item.versionId),
    ['ver-2', 'ver-3'],
  );
});

test('formatRemainingWork keeps hours and adds work-day conversion', () => {
  assert.equal(formatRemainingWork(0), '0h / 0天');
  assert.equal(formatRemainingWork(8), '8h / 1天');
  assert.equal(formatRemainingWork(12), '12h / 1.5天');
  assert.equal(formatRemainingWork(0.5), '0.5h / 0.1天');
});

test('getRiskScoreTone follows Xiaobao warning score thresholds', () => {
  assert.equal(getRiskScoreTone(75), 'danger');
  assert.equal(getRiskScoreTone(55), 'warn');
  assert.equal(getRiskScoreTone(54), 'ok');
});

test('sanitizeRiskInsight filters invalid page refresh suggested actions', () => {
  const result = sanitizeRiskInsight({
    summary: '风险上升',
    why: ['P1 Bug 增加'],
    forecast: '预计延期 1 天',
    suggestedActions: [
      '手动触发页面刷新，等待小宝预警自动更新',
      '优先处理 3 个 P1 Bug，并同步测试负责人复测',
    ],
    ownerHints: ['研发负责人协调修复顺序'],
    generatedAt: '2026-06-30T10:00:00.000Z',
  });

  assert.deepEqual(result.suggestedActions, ['优先处理 3 个 P1 Bug，并同步测试负责人复测']);
});

test('xiaobao warning update state is unread until the current user reads the same risk signature', () => {
  const baseRisk = risk();

  assert.equal(isXiaobaoWarningUpdated(baseRisk, [], 'user-1'), true);

  const readStates = markXiaobaoWarningRead([], 'user-1', baseRisk, '2026-06-30T11:00:00.000Z');

  assert.equal(isXiaobaoWarningUpdated(baseRisk, readStates, 'user-1'), false);
  assert.equal(isXiaobaoWarningUpdated(baseRisk, readStates, 'user-2'), true);
});

test('read current warning state suppresses refresh-triggered AI requests only for the same signature', () => {
  const baseRisk = risk();
  const readStates = markXiaobaoWarningRead([], 'user-1', baseRisk, '2026-06-30T11:00:00.000Z');
  const changedRisk = risk({
    signals: {
      ...baseRisk.signals,
      failedTestCount: 1,
    },
    riskScore: 72,
  });

  assert.equal(
    shouldSkipXiaobaoRiskInsightRequestForReadState(baseRisk, readStates, 'user-1', true),
    true,
  );
  assert.equal(
    shouldSkipXiaobaoRiskInsightRequestForReadState(changedRisk, readStates, 'user-1', true),
    false,
  );
  assert.equal(
    shouldSkipXiaobaoRiskInsightRequestForReadState(baseRisk, readStates, 'user-1', false),
    true,
  );
});

test('xiaobao warning update state becomes unread again when risk facts change', () => {
  const baseRisk = risk();
  const readStates = markXiaobaoWarningRead([], 'user-1', baseRisk, '2026-06-30T11:00:00.000Z');
  const changedRisk = risk({
    riskScore: 69,
    signals: {
      ...baseRisk.signals,
      failedTestCount: 1,
    },
  });

  assert.notEqual(getXiaobaoWarningUpdateSignature(changedRisk), getXiaobaoWarningUpdateSignature(baseRisk));
  assert.equal(isXiaobaoWarningUpdated(changedRisk, readStates, 'user-1'), true);
  assert.equal(getXiaobaoWarningUnreadUpdateCount([baseRisk, changedRisk], readStates, 'user-1'), 1);
});

test('xiaobao warning update state waits until AI suggestion update is complete', () => {
  const baseRisk = risk();
  const readStates = markXiaobaoWarningRead([], 'user-1', baseRisk, '2026-06-30T11:00:00.000Z');
  const changedRisk = risk({
    riskScore: 72,
    aiInsightUpdating: true,
    signals: {
      ...baseRisk.signals,
      failedTestCount: 1,
    },
  });
  const completedRisk = { ...changedRisk, aiInsightUpdating: false };

  assert.equal(isXiaobaoWarningUpdated(changedRisk, readStates, 'user-1'), false);
  assert.equal(getXiaobaoWarningUnreadUpdateCount([changedRisk], readStates, 'user-1'), 0);
  assert.deepEqual(markXiaobaoWarningRead(readStates, 'user-1', changedRisk, '2026-06-30T12:00:00.000Z'), readStates);
  assert.equal(isXiaobaoWarningUpdated(completedRisk, readStates, 'user-1'), true);
});

test('xiaobao warning update state ignores refresh-volatile timing and hour drift', () => {
  const baseRisk = risk({
    forecastReleaseDate: '2026-07-05T10:00:00.000Z',
    dailyEvidence: {
      todayDeliveries: [],
      todayProgress: [
        { id: 'ev-1', title: 'Progress', summary: '开始开发：核心流程', occurredAt: '2026-06-30T10:00:00.000Z' },
      ],
      todayCreations: [],
      todayRisks: [],
      progressNotes: [],
      needsProgressItems: [],
      recentActivityCount: 2,
      totalActivityCount: 1,
      todayActualHours: 1,
      lastActivityAt: '2026-06-30T10:00:00.000Z',
    },
  });
  const readStates = markXiaobaoWarningRead([], 'user-1', baseRisk, '2026-06-30T11:00:00.000Z');
  const refreshDriftRisk = risk({
    forecastReleaseDate: '2026-07-05T11:30:00.000Z',
    signals: {
      ...baseRisk.signals,
      daysToExpectedRelease: 4.2,
    },
    dailyEvidence: {
      ...baseRisk.dailyEvidence!,
      recentActivityCount: 3,
      todayActualHours: 2.5,
      lastActivityAt: '2026-06-30T11:30:00.000Z',
      todayProgress: [
        { id: 'ev-1', title: 'Progress', summary: '开始开发：核心流程', occurredAt: '2026-06-30T11:30:00.000Z' },
      ],
    },
  });

  assert.equal(isXiaobaoWarningUpdated(refreshDriftRisk, readStates, 'user-1'), false);
});

test('markXiaobaoWarningRead replaces only the matching user and version read marker', () => {
  const baseRisk = risk();
  const otherRisk = risk({ versionId: 'ver-2', versionName: 'V2.0' });
  const initial = [
    ...markXiaobaoWarningRead([], 'user-1', baseRisk, '2026-06-30T10:00:00.000Z'),
    ...markXiaobaoWarningRead([], 'user-2', baseRisk, '2026-06-30T10:05:00.000Z'),
    ...markXiaobaoWarningRead([], 'user-1', otherRisk, '2026-06-30T10:10:00.000Z'),
  ];

  const updated = markXiaobaoWarningRead(initial, 'user-1', baseRisk, '2026-06-30T11:00:00.000Z');

  assert.equal(updated.filter((item) => item.userId === 'user-1' && item.versionId === 'ver-1').length, 1);
  assert.equal(updated.find((item) => item.userId === 'user-1' && item.versionId === 'ver-1')?.readAt, '2026-06-30T11:00:00.000Z');
  assert.equal(updated.some((item) => item.userId === 'user-2' && item.versionId === 'ver-1'), true);
  assert.equal(updated.some((item) => item.userId === 'user-1' && item.versionId === 'ver-2'), true);
});
