import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'

import { normalizeRiskObservationDashboard } from '../src/services/riskObservations.js'
import {
  formatExpenseTypeLabel,
  formatRiskDimensionLabel,
  formatRiskObservationTitle,
  formatRiskSignalLabel,
  formatRiskSourceLabel
} from '../src/utils/riskLabels.js'

const dashboardComponent = readFileSync(
  fileURLToPath(new URL('../src/components/dashboard/RiskObservationDashboard.vue', import.meta.url)),
  'utf8'
)
const riskDailyTrendChart = readFileSync(
  fileURLToPath(new URL('../src/components/charts/RiskDailyTrendChart.vue', import.meta.url)),
  'utf8'
)
const overviewViewModel = readFileSync(
  fileURLToPath(new URL('../src/composables/useOverviewView.js', import.meta.url)),
  'utf8'
)
const overviewDisplayModel = readFileSync(
  fileURLToPath(new URL('../src/composables/overviewViewDisplayModel.js', import.meta.url)),
  'utf8'
)
const overviewRangeModel = readFileSync(
  fileURLToPath(new URL('../src/composables/overviewViewRangeModel.js', import.meta.url)),
  'utf8'
)
const overviewTemplate = readFileSync(
  fileURLToPath(new URL('../src/views/OverviewView.vue', import.meta.url)),
  'utf8'
)
const topBarComponent = readFileSync(
  fileURLToPath(new URL('../src/components/layout/TopBar.vue', import.meta.url)),
  'utf8'
)
const topBarOverviewRange = readFileSync(
  fileURLToPath(new URL('../src/components/layout/useTopBarOverviewRange.js', import.meta.url)),
  'utf8'
)
const appShellComposable = readFileSync(
  fileURLToPath(new URL('../src/composables/useAppShell.js', import.meta.url)),
  'utf8'
)
const legacyAppScript = readFileSync(
  fileURLToPath(new URL('../src/scripts/App.js', import.meta.url)),
  'utf8'
)
const riskLabels = readFileSync(
  fileURLToPath(new URL('../src/utils/riskLabels.js', import.meta.url)),
  'utf8'
)

test('risk dashboard normalizes amount, distributions, and ranking fields', () => {
  const dashboard = normalizeRiskObservationDashboard({
    total_observations: 5,
    risk_clue_count: 2,
    feedback_sample_count: 3,
    total_amount: 12800,
    department_distribution: { RiskOps: 3 },
    expense_type_distribution: { travel: 2 },
    risk_type_distribution: { duplicate_invoice: 2 },
    supplier_distribution: { VendorA: 1 },
    employee_grade_distribution: { P6: 2 },
    top_departments: [{ name: 'RiskOps', count: 3, amount: 8800 }],
    top_employees: [{ name: 'RiskUser', count: 2, amount: 6200 }],
    top_suppliers: [{ name: 'VendorA', count: 1, amount: 1200 }],
    top_expense_types: [{ name: 'travel', count: 2, amount: 4600 }],
    top_rules: [{ name: 'policy.duplicate_invoice', count: 2, amount: 3000 }]
  })

  assert.equal(dashboard.totalAmount, 12800)
  assert.equal(dashboard.riskClueCount, 2)
  assert.equal(dashboard.feedbackSampleCount, 3)
  assert.equal(dashboard.departmentDistribution.RiskOps, 3)
  assert.equal(dashboard.expenseTypeDistribution.travel, 2)
  assert.equal(dashboard.riskTypeDistribution.duplicate_invoice, 2)
  assert.equal(dashboard.supplierDistribution.VendorA, 1)
  assert.equal(dashboard.employeeGradeDistribution.P6, 2)
  assert.equal(dashboard.topDepartments[0].amount, 8800)
  assert.equal(dashboard.topRules[0].name, 'policy.duplicate_invoice')
})

test('risk dashboard renders multi-dimension and ranking panels', () => {
  assert.match(dashboardComponent, /risk-dimension-grid/)
  assert.match(dashboardComponent, /risk-composition-panel/)
  assert.match(dashboardComponent, /risk-ranking-visual/)
  assert.match(dashboardComponent, /risk-ranking-chart-block/)
  assert.match(dashboardComponent, /\.risk-ranking-panel\s*\{\s*grid-column:\s*span 12;/)
  assert.match(dashboardComponent, /rankingChartItems/)
  assert.match(dashboardComponent, /rankingDetailGroups/)
  assert.match(dashboardComponent, /departmentDistribution/)
  assert.match(dashboardComponent, /expenseTypeDistribution/)
  assert.match(dashboardComponent, /supplierDistribution/)
  assert.match(dashboardComponent, /employeeGradeDistribution/)
  assert.match(dashboardComponent, /topDepartments/)
  assert.match(dashboardComponent, /topEmployees/)
  assert.match(dashboardComponent, /topSuppliers/)
  assert.match(dashboardComponent, /topRules/)
  assert.doesNotMatch(dashboardComponent, /risk-effect-panel/)
  assert.doesNotMatch(dashboardComponent, /risk-recent-panel/)
})

test('risk dashboard localizes backend metric keys before rendering', () => {
  assert.notEqual(formatRiskSignalLabel('duplicate_invoice'), 'duplicate_invoice')
  assert.notEqual(formatRiskSignalLabel('budget_pressure'), 'budget_pressure')
  assert.notEqual(formatRiskSignalLabel('missing_material'), 'missing_material')
  assert.notEqual(formatExpenseTypeLabel('travel'), 'travel')
  assert.notEqual(formatRiskSourceLabel('rule_center'), 'rule_center')
  assert.notEqual(formatRiskSourceLabel('financial_risk_graph'), 'financial_risk_graph')
  assert.notEqual(formatRiskDimensionLabel('simulation', 'risk_type'), 'simulation')
  assert.notEqual(formatRiskDimensionLabel('policy.duplicate_invoice', 'rule'), 'policy.duplicate_invoice')
  assert.notEqual(
    formatRiskObservationTitle({ title: 'policy.duplicate_invoice', riskSignal: 'duplicate_invoice' }),
    'policy.duplicate_invoice'
  )
  assert.match(riskLabels, /travel:/)
  assert.match(riskLabels, /rule_center:/)
  assert.match(overviewDisplayModel, /formatRiskSignalLabel/)
  assert.match(overviewViewModel, /riskCompositionLegend/)
  assert.match(overviewViewModel, /signalDistribution/)
  assert.doesNotMatch(overviewViewModel, /formatRiskSourceLabel/)
  assert.doesNotMatch(dashboardComponent, /formatRiskObservationTitle/)
  assert.doesNotMatch(dashboardComponent, /text\.replace\(\s*\/_\/g/)
})

test('risk dashboard follows the top overview range without card-level selectors', () => {
  assert.match(overviewViewModel, /const topRangeDays = computed/)
  assert.match(overviewViewModel, /windowDays: topRangeDays\.value/)
  assert.match(overviewViewModel, /options\.activeRange/)
  assert.doesNotMatch(overviewViewModel, /activeRiskWindowDays/)
  assert.doesNotMatch(overviewViewModel, /setRiskWindowDays/)
  assert.doesNotMatch(overviewTemplate, /:window-options="riskWindowOptions"/)
  assert.doesNotMatch(overviewTemplate, /:active-window-days="activeRiskWindowDays"/)
  assert.doesNotMatch(overviewTemplate, /@update:window-days="setRiskWindowDays"/)
  assert.doesNotMatch(dashboardComponent, /EnterpriseSelect/)
  assert.doesNotMatch(dashboardComponent, /risk-window-select/)
  assert.doesNotMatch(dashboardComponent, /emit\('update:windowDays', \$event\)/)
  assert.match(dashboardComponent, /dashboard\.windowDays/)
  assert.match(dashboardComponent, /RiskDailyTrendChart/)
})

test('overview custom date defaults use current year instead of hard-coded legacy dates', () => {
  assert.match(topBarComponent, /createCurrentYearDateRange/)
  assert.match(topBarComponent, /useTopBarOverviewRange/)
  assert.match(topBarOverviewRange, /formatDateValue/)
  assert.match(topBarOverviewRange, /useTopBarOverviewRange\(props, emit\)/)
  assert.match(appShellComposable, /createCurrentYearDateRange\(\)/)
  assert.match(legacyAppScript, /createCurrentYearDateRange\(\)/)
  assert.doesNotMatch(topBarComponent, /2024-07-06|2024-07-12/)
  assert.doesNotMatch(topBarOverviewRange, /2024-07-06|2024-07-12/)
  assert.doesNotMatch(appShellComposable, /2024-07-06|2024-07-12/)
  assert.doesNotMatch(legacyAppScript, /2024-07-06|2024-07-12/)
})

test('risk daily trend is bucketed for long ranges and keeps chart labels readable', () => {
  assert.match(overviewRangeModel, /RISK_DAILY_TREND_MAX_BUCKETS = 14/)
  assert.match(overviewViewModel, /aggregateRiskDailyTrendRows/)
  assert.match(overviewRangeModel, /Math\.ceil\(normalizedRows\.length \/ maxBuckets\)/)
  assert.match(overviewRangeModel, /buildRiskTrendBucketLabel/)
  assert.doesNotMatch(overviewViewModel, /rows\.slice\(-7\)/)
  assert.match(riskDailyTrendChart, /const barWidth = computed/)
  assert.match(riskDailyTrendChart, /barMaxWidth: 14/)
  assert.match(riskDailyTrendChart, /bottomGridSize/)
  assert.match(riskDailyTrendChart, /replace\('~', '\\n~'\)/)
})

test('risk dashboard shows loading overlay and realtime refresh status', () => {
  assert.match(overviewTemplate, /dashboard-loading-state/)
  assert.match(overviewTemplate, /floating/)
  assert.match(overviewTemplate, /TableLoadingState/)
  assert.match(overviewTemplate, /activeDashboardLoadingText/)
  assert.match(dashboardComponent, /risk-dashboard-loading-state/)
  assert.match(dashboardComponent, /floating/)
  assert.match(dashboardComponent, /TableLoadingState/)
  assert.match(dashboardComponent, /loadingLabel/)
  assert.match(dashboardComponent, /lastUpdatedLabel/)
  assert.match(dashboardComponent, /lastUpdatedAt/)
  assert.match(overviewViewModel, /riskDashboardLastUpdatedAt/)
  assert.match(overviewViewModel, /startRiskDashboardRealtimeRefresh/)
  assert.match(overviewViewModel, /setInterval/)
  assert.match(overviewViewModel, /document\.visibilityState === 'hidden'/)
  assert.match(overviewViewModel, /riskDashboardRequestSeq/)
  assert.match(overviewTemplate, /:last-updated-at="riskDashboardLastUpdatedAt"/)
})

test('overview dashboards are loaded on demand instead of all at once', () => {
  assert.match(overviewViewModel, /const activeDashboardKey = computed/)
  assert.match(overviewViewModel, /const loadActiveDashboard = \(\) =>/)
  assert.doesNotMatch(
    overviewViewModel,
    /onMounted\(\(\) => \{\s*void loadFinanceDashboard\(\)\s*void loadSystemDashboard\(\)\s*void loadRiskDashboard\(\)\s*void loadDigitalEmployeeDashboard\(\)/
  )
  assert.match(overviewViewModel, /watch\(activeDashboardKey/)
  assert.match(overviewViewModel, /activeDashboardKey\.value === 'risk'/)
  assert.match(overviewViewModel, /startRiskDashboardRealtimeRefresh\(\)/)
  assert.match(overviewViewModel, /stopRiskDashboardRealtimeRefresh\(\)/)
})
