export default {
  common: {
    appName: 'xSight',
    save: 'Save',
    cancel: 'Cancel',
    delete: 'Delete',
    edit: 'Edit',
    create: 'Create',
    search: 'Search',
    confirm: 'Confirm',
    actions: 'Actions',
    enabled: 'Enabled',
    disabled: 'Disabled',
    status: 'Status',
    type: 'Type',
    description: 'Description',
    name: 'Name',
    id: 'ID',
    createdAt: 'Created At',
    yes: 'Yes',
    no: 'No',
    loading: 'Loading...',
    noData: 'No data',
    showing: 'showing',
    more: 'more',
    collapse: 'collapse',
    success: 'Success',
    error: 'Error',
    duplicate: 'Duplicate',
    theme: 'Theme',
    language: 'Language',
    logout: 'Logout',
    classic: 'Classic',
    amber: 'Amber',
    refresh: 'Refresh',
    close: 'Close',
    updated: 'Updated',
    created: 'Created',
    deleted: 'Deleted',
    failed: 'Failed',
    monitoring: 'MONITORING',
    infrastructure: 'INFRASTRUCTURE',
    detection: 'DETECTION',
    settings: 'SETTINGS',
  },
  login: {
    title: 'Sign in to xSight',
    subtitle: 'DDoS Detection & Response Platform',
    username: 'Username',
    password: 'Password',
    signIn: 'Sign In',
    invalidCredentials: 'Invalid username or password',
  },
  nav: {
    dashboard: 'Dashboard',
    nodes: 'Nodes',
    templates: 'Templates',
    prefixes: 'Watch Prefixes',
    thresholds: 'Thresholds',
    responses: 'Responses',
    webhooks: 'Webhooks',
    attacks: 'Attacks',
    mitigations: 'Active Mitigations',
    users: 'Users',
    auditLog: 'Audit Log',
    trafficOverview: 'Traffic Overview',
    dynamicDetection: 'Dynamic Detection',
    settings: 'Settings',
    webhookConnectors: 'Webhook Connectors',
    xdropConnectors: 'xDrop Connectors',
    shellConnectors: 'Shell Scripts',
    bgpConnectors: 'BGP Connectors',
  },
  nodes: {
    lastAckTip: 'Last time the node successfully applied a config push',
    title: 'Node Management',
    addNode: 'Add Node',
    nodeId: 'Node ID',
    apiKey: 'API Key',
    mode: 'Mode',
    online: 'Online',
    offline: 'Offline',
    configStatus: 'Config Status',
    synced: 'Synced',
    pending: 'Pending',
    failed: 'Failed',
    deliveryVersion: 'Delivery Version',
    current: 'Current',
    applied: 'Applied',
    drift: 'Drift',
    lastAck: 'Last ACK',
  },
  prefixes: {
    title: 'Watch Prefix Management',
    addPrefix: 'Add Prefix',
    prefix: 'Prefix (CIDR)',
    parent: 'Parent Prefix',
    ipGroup: 'IP Group',
  },
  thresholds: {
    title: 'Threshold Rules',
    addThreshold: 'Add Threshold',
    domain: 'Domain',
    direction: 'Direction',
    decoder: 'Decoder',
    unit: 'Unit',
    comparison: 'Comparison',
    value: 'Value',
    inheritable: 'Inheritable',
    pctRange: 'Valid range: 1–100',
    pctWarning: 'pct threshold must be between 1 and 100',
    response: 'Response',
    templates: 'Threshold Templates',
    usedBy: 'Used by',
    addRule: 'Add Rule',
    globalSubnetOnly: 'Global prefix (0.0.0.0/0) only supports subnet-level rules',
    defaultResponse: 'Default Response',
    noResponse: 'None',
    useDefault: '-- (use default)',
    receives: 'Receives',
    sends: 'Sends',
    over: 'Over',
    under: 'Under',
  },
  responses: {
    periodicExplanation: 'Runs with attack updates, approximately every 5 minutes.',
    conditionsReadOnly: 'Conditions failed to load. This save will not modify conditions.',
    conditionIncomplete: 'Condition row {row} is incomplete. Complete or remove it.',
    actionSavedDisabled: 'The action has been saved in a disabled state.',
    title: 'Response Configuration',
    addResponse: 'Add Response',
    actions: 'Actions',
    addAction: 'Add Action',
    editAction: 'Edit Action',
    actionType: 'Action Type',
    executionPolicy: 'Execution Policy',
    priority: 'Priority',
    config: 'Config (JSON)',
    preconditions: 'Preconditions',
    preconditionsComingSoon: 'Preconditions: coming soon',
    addCondition: 'Add Condition',
    boundTemplates: 'Bound Templates',
    onDetected: 'When Attack Detected',
    onExpired: 'When Attack Expired',
    connector: 'Connector',
    connectorName: 'Connector',
    selectConnector: 'Select connector',
    runMode: 'Run Mode',
    periodSeconds: 'Period (seconds)',
    execution: 'Execution',
    xdropAction: 'xDrop Action',
    xdropDecoderScope: 'Decoder compatibility',
    xdropDecoderScopeBody: 'xDrop dispatch is gated by the attack decoder. This action will fire only when the triggering threshold rule uses decoder = tcp / tcp_syn / tcp_ack / tcp_rst / tcp_fin / udp / icmp / fragment / gre / esp / igmp / bad_fragment / invalid. For the ip / ip_other aggregate decoders xSight will skip this action (skip_reason=decoder_not_xdrop_compatible) — use a BGP action for L3 mitigation instead. Anomaly decoders (bad_fragment / invalid) use xDrop v3 anomaly-bit matching and support both drop and rate_limit; xDrop rejects IPv6 bad_fragment.',
    filterFields: 'Filter Fields',
    filterFieldsHint: 'Checked fields that apply to the attack type become rule conditions; empty values are not sent.',
    filterFieldsRequired: 'At least one filter field is required',
    rateLimitPPS: 'Rate Limit (PPS)',
    rateLimitRequired: 'Rate limit must be greater than 0',
    targetNodes: 'Target Nodes',
    targetNodesHint: 'Empty = all nodes',
    shellExtraArgs: 'Extra Arguments',
    shellExtraArgsPlaceholder: 'Additional command arguments',
    unblockDelay: 'Unblock Delay (min)',
    unblockDelayHint: 'How long this xSight keeps holding the rule after the attack ends. 0 = release immediately. XDrop deletes the rule after its last holder releases it.',
    withdrawDelay: 'Withdraw Delay (min)',
    withdrawDelayHint: 'How long to keep the BGP route after attack expires. 0 = withdraw immediately.',
    protocolPinnedTip: 'Protocol follows the attack type, checked or not; anomaly rules carry no protocol.',
  },
  webhooks: {
    title: 'Webhook Management',
    addWebhook: 'Add Webhook',
    url: 'URL',
    events: 'Events',
    headers: 'Headers (JSON)',
  },
  attacks: {
    expiredNotHeld: 'Attack #{id} expired, but no hold was applied: it was not in live detection (e.g. after a controller restart)',
    expiredGlobal: 'Attack #{id} expired; hold requested per the global setting',
    expiredNoHold: 'Attack #{id} expired; no hold requested',
    expiredHeld: 'Attack #{id} expired; hold requested for up to {n} s',
    expireHoldInvalid: 'Enter a whole number from 0 to 86400, or leave it empty',
    expireHoldPlaceholder: 'Hold seconds, e.g. 600 (empty = use global setting, 0 = no hold, max 86400)',
    expireConfirm: 'Expire attack #{id} ({target})? During the hold, new alerts for {target} with the same decoder and direction are suppressed. The hold ends early once detection no longer sees this attack, and a controller restart clears it.',
    expireTitle: 'Expire attack',
    title: 'Attack Records',
    active: 'Active Attacks',
    all: 'All Attacks',
    dstIp: 'Target IP',
    srcIpLabel: 'Source IP (Outbound)',
    direction: 'Direction',
    decoder: 'Decoder',
    attackType: 'Attack Type',
    severity: 'Severity',
    peakPps: 'Peak PPS',
    peakBps: 'Peak BPS',
    peak: 'Peak',
    timer: 'Timer',
    breaching: 'Breaching',
    expiringIn: 'Expiring in',
    triggerRule: 'Trigger Rule',
    bgpRouting: 'BGP Routing',
    xdropFiltering: 'xDrop Filtering',
    forceWithdraw: 'Force Withdraw',
    forceUnblock: 'Force Unblock',
    forceRemoved: 'Force removed',
    confirmForceWithdraw: 'Force withdraw this route?',
    confirmForceUnblock: 'Force unblock this rule?',
    confirmDetachFromShared: 'This route is shared with {remaining} other attack(s). Force Remove withdraws the entire route, so those attacks also lose protection. Continue?',
    announcedAt: 'Announced At',
    statusActive: 'Active',
    statusDelayed: 'Delayed',
    statusPending: 'Pending',
    statusFailed: 'Failed',
    startedAt: 'Started At',
    endedAt: 'Ended At',
    duration: 'Duration',
    nodeSources: 'Node Sources',
    reasonCodes: 'Reason Codes',
    confidence: 'Confidence',
    expire: 'Expire',
    actionsLog: 'Actions Log',
    triggerPhase: 'Trigger Phase',
    statusCode: 'Status Code',
    durationMs: 'Duration (ms)',
    bgpRole: 'BGP Role',
    bgpRoleTriggered: 'triggered',
    bgpRoleAttached: 'attached',
    viewDetail: 'Detail',
    actionDetail: 'Action Detail',
    sensorLogs: 'Sensor Logs',
    sensorLogsNote: 'Sampled Top-N Flow Summary (per-second top-100 by packets, subject to sampling rate)',
    srcIp: 'Source IP',
    srcPort: 'SPort',
    dstPort: 'DPort',
    protocol: 'Protocol',
    tcpFlags: 'TCP Flags',
    packets: 'Packets',
    bytes: 'Bytes',
    topSrcIps: 'Top Source IPs',
    topSrcPorts: 'Top Source Ports',
    topDstPorts: 'Top Dest Ports',
    flowPercent: 'Percent',
    noFlowData: 'No flow data available for this attack',
    flowDataExpired: 'Flow logs have expired (beyond retention period). Only the last 7 days of sampled data are retained.',
    globalNoFlowDetail: 'Global attacks have no flow detail — 0.0.0.0/0 is a virtual global prefix covering all monitored IPv4 + IPv6 traffic.',
    time: 'Time',
    responseBody: 'Response Body',
    showRaw: 'Show Raw',
    showFormatted: 'Show Formatted',
    errorMessage: 'Error Message',
    critical: 'Critical',
    high: 'High',
    medium: 'Medium',
    low: 'Low',
    subtitle: 'Detection events and incident history',
    showingTop: 'Showing top {count} by peak PPS',
    totalActive: 'total active',
    global: 'GLOBAL',
    outbound: '↑ OUT',
    inbound: '↓ IN',
    confirmReleaseShared: 'Release the hold of this attack and action? This xSight has {n} other holder(s); the rule stays.',
    confirmReleaseLast: 'Release the hold of this attack and action? Other systems may still hold the rule.',
    holdReleased: 'Hold released',
    logNote: 'Note',
    logSkipReason: 'Skip reason',
    logDetail: 'Detail',
  },
  users: {
    title: 'User Management',
    addUser: 'Add User',
    username: 'Username',
    password: 'Password',
    role: 'Role',
    admin: 'Admin',
    operator: 'Operator',
    viewer: 'Viewer',
    newPassword: 'New Password',
    passwordPlaceholder: 'Leave blank to keep current',
    created: 'User created',
    updated: 'Updated',
    deleted: 'Deleted',
    confirmDelete: 'Delete this user?',
    createFailed: 'Create failed',
    updateFailed: 'Update failed',
    deleteFailed: 'Delete failed',
  },
  dynDetect: {
    title: 'Dynamic Detection',
    infoTitle: 'How Dynamic Detection Works',
    infoDesc: 'Dynamic detection builds per-prefix traffic profiles using EWMA (Exponentially Weighted Moving Average). It divides each week into 168 hourly slots and learns expected traffic levels over multiple weeks. When actual traffic deviates beyond a progressive threshold, an alert is raised.',
    infoEwma: 'EWMA smooths traffic history — recent data is weighted more heavily via the alpha parameter.',
    infoSlots: '168 slots per week (24h x 7d) — each slot tracks its own expected baseline.',
    infoDeviation: 'Progressive deviation — newly learned profiles use a wider tolerance that narrows as more samples accumulate.',
    infoMinThreshold: 'Minimum threshold ensures low-traffic prefixes do not trigger false positives.',
    configTitle: 'Configuration',
    enabled: 'Enable Dynamic Detection',
    deviationMax: 'Deviation Max (%)',
    deviationMaxTip: 'Maximum allowed deviation from expected value for new profiles with few samples (e.g. 200 = 2x expected).',
    deviationMin: 'Deviation Min (%)',
    deviationMinTip: 'Minimum deviation for mature profiles with enough stable weeks (e.g. 50 = 1.5x expected).',
    stableWeeks: 'Stable Weeks',
    stableWeeksTip: 'Number of weeks of data required before the profile is considered mature and deviation narrows to min.',
    minPps: 'Min PPS',
    minPpsTip: 'Minimum packets-per-second threshold. Dynamic threshold will not go below this value.',
    minBps: 'Min BPS',
    minBpsTip: 'Minimum bits-per-second threshold. Dynamic threshold will not go below this value.',
    ewmaAlpha: 'EWMA Alpha',
    ewmaAlphaTip: 'Smoothing factor (0.01-0.99). Higher values react faster to recent traffic changes, lower values are more stable.',
    profileTitle: 'Profile Status',
    refresh: 'Refresh',
    currentSlot: 'Current Slot',
    totalPrefixes: 'Total Prefixes',
    activated: 'Activated',
    learning: 'Learning',
    prefix: 'Prefix',
    currentPps: 'Current PPS',
    expectedPps: 'Expected PPS',
    dynamicThreshold: 'Dynamic Threshold',
    sampleWeeks: 'Sample Weeks',
    statusLearning: 'Learning',
    statusNormal: 'Normal',
    statusExceeded: 'Exceeded',
  },
  flow: {
    title: 'Flow Configuration',
    addListener: 'Add Listener',
    listenAddress: 'Listen Address',
    protocolMode: 'Protocol',
    sources: 'Sources',
    manageSources: 'Flow Config',
    addSource: 'Add Source',
    deviceIp: 'Device IP',
    sampleMode: 'Sample Mode',
    sampleRate: 'Sample Rate',
    decodeErrors: 'Decode Errors',
    unknownExporter: 'Unknown Exporter',
    templateMisses: 'Template Misses',
    sourceStatus: 'Status',
    recordsDecoded: 'Records Decoded',
    lastReport: 'Last Report',
    uptime: 'Uptime',
  },
  traffic: {
    prefixFilterTip: 'Filters this chart only; the cards and the Top table are not filtered by prefix',
    prefix: 'Prefix',
    allNodes: 'All Nodes',
    totalPps: 'Total PPS',
    totalBps: 'Total BPS',
    activeNodes: 'Active Nodes',
    activePrefixes: 'Active Prefixes',
    topPrefixes: 'Top Prefixes',
    totalTrend: 'Total Traffic Trend',
  },
  mitigations: {
    subtitle: 'Active BGP routes and xDrop filtering rules',
    prefix: 'Prefix',
    routeMap: 'Route Map',
    bgpConnector: 'BGP Connector',
    ruleId: 'Rule ID',
    xdropConnector: 'xDrop Connector',
    forceFailed: 'Force remove failed',
    timer: 'Timer',
    elapsed: 'Elapsed',
    remaining: 'Remaining',
    detail: 'Mitigation Detail',
    bgpRoute: 'BGP Route',
    xdropRule: 'xDrop Rule',
    attack: 'Attack',
    targetIp: 'Target IP',
    connector: 'Connector',
    configuration: 'Configuration',
    timeline: 'Execution Timeline',
    noTimeline: 'No execution logs',
    action: 'Action',
    // v1.2 PR-5 orphan banner
    orphanBannerTitle: '{count} orphan BGP route(s) detected',
    orphanBannerDesc: 'These FRR routes have no active attack. They may be legacy state from a prior xSight version, or routes managed outside xSight. Review and either Force Withdraw (removes from FRR) or Dismiss (keeps route, stops showing).',
    confirmForceWithdrawShared: 'This route has {count} active attacks attached. Force Withdraw will detach all of them and withdraw the announcement. Continue?',
    confirmOrphanForceWithdraw: 'Force withdraw this orphan route from FRR?',
    confirmOrphanDismiss: 'Dismiss this orphan? The route stays in FRR, but xSight stops tracking it.',
    dismiss: 'Dismiss',
    dismissed: 'Orphan dismissed',
    // View dismissed orphans (v1.2)
    attachedAttacks: 'Attached attacks ({count})',
    attachedCurrent: 'active',
    viewDismissedOrphans: 'View dismissed orphans ({count})',
    noDismissedOrphans: 'No dismissed orphans',
    dismissedOnUpgrade: 'Dismissed on upgrade',
    detectedAt: 'Detected at',
    undismiss: 'Un-dismiss',
    undismissed: 'Re-surfaced as orphan',
    confirmUndismiss: 'Re-surface this as an orphan so it appears in the banner again?',
    shared: 'Shared',
    sharedTip: 'This xSight has {n} other holder(s) of this rule.',
    createFailed: 'Create failed',
    createFailedTip: 'Create result unknown; closed automatically after the attack ends. Internal ID: {id}',
    failedRetryTip: 'Withdraw not completed; retried automatically.',
    connectorDisabled: 'Disabled',
    connectorDisabledTip: 'Connector disabled: withdraw waits until it is enabled; force unblock is rejected.',
    delivery: 'Delivery',
    deliveryTip: 'Active means XDrop has accepted the rule. Per-node state is looked up once when this panel opens.',
    deliveryRefresh: 'Refresh',
    deliveryNotFound: 'Not on XDrop',
    deliveryUnavailable: 'Connector unavailable',
    deliveryFailed: 'Lookup failed',
    deliveryTimeout: 'Lookup timed out',
    deliveryNone: 'No nodes',
    deliveryStateConfirmed: 'Confirmed',
    deliveryStatePending: 'Pending',
    deliveryStateDisabled: 'Disabled',
    deliveryStateOffline: 'Offline',
    deliveryStateIncompatible: 'Incompatible',
    deliveryStateOverloaded: 'Overloaded',
    deliveryStateRejected: 'Rejected',
    forceTimeout: 'Request timed out; the server may still be processing.',
  },
  webhookConnectors: {
    timeoutTip: 'Request timeout in milliseconds (1–10000); a webhook call never waits longer than 10 s',
    timeout: 'Timeout',
    title: 'Webhook Connectors',
    add: 'Add Connector',
    edit: 'Edit Connector',
    url: 'URL',
    method: 'Method',
    headers: 'Headers (JSON)',
    global: 'Global',
    test: 'Test',
    testSuccess: 'Webhook test successful',
    testFailed: 'Webhook test failed',
    confirmDelete: 'Delete this webhook connector?',
  },
  templates: {
    recommendInboundOnly: 'Baselines count inbound traffic only',
    prefixLevel: '(prefix-level)',
  },
  xdropConnectors: {
    apiUrlHint: 'Enter a versioned base URL, such as https://xdrop.example.com:8000/api/v1; the system appends /rules.',
    nameRequired: 'Name is required',
    apiUrlRequired: 'API URL is required',
    apiKeyRequired: 'API key is required',
    title: 'xDrop Connectors',
    add: 'Add Connector',
    edit: 'Edit Connector',
    apiUrl: 'API URL',
    apiKey: 'API Key',
    apiKeyPlaceholder: 'Leave blank to keep current',
    test: 'Test',
    testSuccess: 'Connected as {name}',
    testFailed: 'Connection test failed',
    confirmDelete: 'Delete this xDrop connector?',
    decoderScopeTitle: 'xDrop decoder compatibility',
    decoderScopeBody: 'xDrop actions fire for threshold rules whose decoder is tcp / tcp_syn / tcp_ack / tcp_rst / tcp_fin / udp / icmp / fragment / gre / esp / igmp / bad_fragment / invalid. Rules using the ip / ip_other aggregate decoders are mitigated by BGP instead (use a BGP null-route action in the response). Anomaly decoders (bad_fragment / invalid) use xDrop v3 anomaly-bit matching and support both drop and rate_limit; xDrop rejects IPv6 bad_fragment.',
    timeout: 'Timeout',
    timeoutTip: 'Wait limit per XDrop request, 1–30000 ms (default 10000). Used by create, withdraw, force unblock and delivery lookup; larger values make force unblock and lookup slower.',
    apiKeyTip: 'Key of the machine credential created in XDrop for this xSight.',
    deleteInUse: 'Still used by rules or actions, listed under Mitigations and Responses.',
    deleteFailed: 'Delete failed',
  },
  bgpConnectors: {
    title: 'BGP Connectors',
    add: 'Add Connector',
    edit: 'Edit Connector',
    test: 'Test',
    routes: 'Routes',
    bgpRoutes: 'BGP Routes',
    bgpAsn: 'BGP ASN',
    vtyshPath: 'vtysh Path',
    connectionOk: 'BGP connection OK',
    testFailed: 'Test failed',
    fetchRoutesFailed: 'Failed to fetch routes',
    deleteConfirm: 'Delete this BGP connector?',
  },
  shellConnectors: {
    timeoutTip: 'Execution timeout in milliseconds (1–30000); each action is capped at about 30 s in total',
    timeout: 'Timeout',
    title: 'Shell Script Connectors',
    add: 'Add Connector',
    edit: 'Edit Connector',
    command: 'Command',
    defaultArgs: 'Default Args',
    commandHint: 'Command must start with an absolute path (e.g. /usr/local/bin/script.sh)',
    commandValidation: 'Command must start with /',
    nameRequired: 'Name is required',
    commandRequired: 'Command is required',
    confirmDelete: 'Delete this shell connector?',
  },
  audit: {
    page: 'Page {n}',
    nextPage: 'Next',
    prevPage: 'Previous',
    endTime: 'To',
    startTime: 'From',
    allUsers: 'All users',
    entityTypePlaceholder: 'Entity type, e.g. watch_prefix',
    title: 'Audit Log',
    entityType: 'Entity Type',
    entityId: 'Entity ID',
    action: 'Action',
    diff: 'Changes',
    deliveryVersion: 'Delivery Version',
    user: 'User',
  },
}
