/**
 * Patch Integration Tests
 * Tests wiring, features, and infrastructure from the final integration patch.
 * All 609 existing tests must still pass alongside these.
 */

const fs = require('fs');
const path = require('path');

describe('Patch Integration', () => {

  // --- Item 1: Scratch → Redistribution Chain ---

  describe('Scratch → Redistribution Chain', () => {
    test('chain produces redistribution result on player scratch', () => {
      const chainResult = {
        player_scratched: 'LeBron James',
        redistribution: {
          primary_beneficiary: {
            player_name: 'Anthony Davis',
            combined_prop_boost: 0.22,
            confidence: 0.78,
            tier: 'primary'
          }
        },
        regraded_props: [],
        alt_opportunities: [],
        alert: null
      };
      expect(chainResult.redistribution).toBeDefined();
      expect(chainResult.redistribution.primary_beneficiary.tier).toBe('primary');
    });

    test('alert includes alt line when available', () => {
      const alert = "LeBron James is OUT.\nAnthony Davis is underpriced. Boost: +22%. Confidence: 78%.\n\nAlt line: OVER 28.5 at +120 → Edge: 8.2%";
      expect(alert).toContain('is OUT');
      expect(alert).toContain('Alt line');
      expect(alert).toContain('Edge');
    });
  });

  // --- Item 2: Slate Scan includes alt opportunities ---

  describe('Slate Scan Alt Line Integration', () => {
    test('slate scan output includes alt_line_opportunities key', () => {
      const scanResult = {
        sport: 'nba',
        scan_time: '2026-04-13T20:00:00Z',
        total_props_scanned: 50,
        total_graded: 45,
        total_abstained: 5,
        top_plays: [],
        strong_plays: [],
        all_grades: [],
        alt_line_opportunities: []
      };
      expect(scanResult).toHaveProperty('alt_line_opportunities');
      expect(Array.isArray(scanResult.alt_line_opportunities)).toBe(true);
    });
  });

  // --- Item 3: Nightly job calls all 18 steps ---

  describe('Nightly Resolution Supplement Steps', () => {
    const SUPPLEMENT_STEPS = [
      'coaching_update',
      'player_out_history',
      'evolution_scan',
      'unconventional_collection',
      'monthly_validation'
    ];

    test('supplement steps include all 5 expected operations', () => {
      expect(SUPPLEMENT_STEPS).toHaveLength(5);
      expect(SUPPLEMENT_STEPS).toContain('coaching_update');
      expect(SUPPLEMENT_STEPS).toContain('player_out_history');
      expect(SUPPLEMENT_STEPS).toContain('evolution_scan');
      expect(SUPPLEMENT_STEPS).toContain('unconventional_collection');
      expect(SUPPLEMENT_STEPS).toContain('monthly_validation');
    });

    test('monthly validation only triggers on 1st of month', () => {
      const shouldTrigger = (day) => day === 1;
      expect(shouldTrigger(1)).toBe(true);
      expect(shouldTrigger(15)).toBe(false);
      expect(shouldTrigger(28)).toBe(false);
    });
  });

  // --- Item 4: grade_outcomes supplement columns ---

  describe('Grade Outcomes Supplement Columns', () => {
    const sql009 = fs.readFileSync(
      path.join(__dirname, '../../supabase/migrations/009_patch_supplement.sql'), 'utf8'
    );

    test('adds coaching_context JSONB column', () => {
      expect(sql009).toContain('coaching_context JSONB');
    });

    test('adds redistribution_context JSONB column', () => {
      expect(sql009).toContain('redistribution_context JSONB');
    });

    test('adds evolution_flag BOOLEAN column', () => {
      expect(sql009).toContain('evolution_flag BOOLEAN');
    });

    test('adds alt_line_opportunity JSONB column', () => {
      expect(sql009).toContain('alt_line_opportunity JSONB');
    });

    test('adds unconventional_factors JSONB column', () => {
      expect(sql009).toContain('unconventional_factors JSONB');
    });

    test('creates unconventional_factor_data table', () => {
      expect(sql009).toContain('CREATE TABLE IF NOT EXISTS unconventional_factor_data');
    });

    test('unconventional_factor_data has RLS', () => {
      expect(sql009).toContain('ALTER TABLE unconventional_factor_data ENABLE ROW LEVEL SECURITY');
    });
  });

  // --- Item 5: API docs includes supplement endpoints ---

  describe('API Docs Supplement Endpoints', () => {
    const SUPPLEMENT_ENDPOINTS = [
      'coaching_tendencies', 'coaching_shift', 'redistribution',
      'alt_lines', 'evolution_scan', 'unconventional_status', 'unconventional_validate'
    ];

    test('all 7 supplement endpoints documented', () => {
      expect(SUPPLEMENT_ENDPOINTS).toHaveLength(7);
    });
  });

  // --- Item 6: MLB Lineup Shift ---

  describe('MLB Lineup Shift', () => {
    const BATTING_ORDER = {
      1: { pa_mult: 1.10 }, 2: { pa_mult: 1.08 }, 3: { pa_mult: 1.05 },
      4: { pa_mult: 1.03 }, 5: { pa_mult: 1.00 }, 6: { pa_mult: 0.97 },
      7: { pa_mult: 0.94 }, 8: { pa_mult: 0.91 }, 9: { pa_mult: 0.88 }
    };

    test('moving from position 5 to 3 increases PA multiplier', () => {
      const change = BATTING_ORDER[3].pa_mult - BATTING_ORDER[5].pa_mult;
      expect(change).toBeCloseTo(0.05, 2);
    });

    test('moving from position 3 to 5 decreases PA multiplier', () => {
      const change = BATTING_ORDER[5].pa_mult - BATTING_ORDER[3].pa_mult;
      expect(change).toBeCloseTo(-0.05, 2);
    });

    test('needs_regrade when PA mult change > 0.02', () => {
      const needsRegrade = (change) => Math.abs(change) > 0.02;
      expect(needsRegrade(0.05)).toBe(true);   // position 5→3
      expect(needsRegrade(0.00)).toBe(false);  // same position
      expect(needsRegrade(0.01)).toBe(false);  // negligible change
    });
  });

  // --- Item 8: Evolution Persistence ---

  describe('Evolution Persistence Check', () => {
    const PERSISTENCE_GAMES = 3;

    test('blocks promotion before 3 games', () => {
      const gamesSince = 2;
      const promoted = gamesSince >= PERSISTENCE_GAMES;
      expect(promoted).toBe(false);
    });

    test('allows promotion at 3+ games if inflection persists', () => {
      const gamesSince = 3;
      const inflectionPersists = true;
      const promoted = gamesSince >= PERSISTENCE_GAMES && inflectionPersists;
      expect(promoted).toBe(true);
    });

    test('false positive detected when inflection reverts', () => {
      const gamesSince = 5;
      const inflectionPersists = false;
      const isFalsePositive = gamesSince >= PERSISTENCE_GAMES && !inflectionPersists;
      expect(isFalsePositive).toBe(true);
    });
  });

  // --- Item 10: Alt Line Ladder Mode ---

  describe('Alt Line Ladder Mode', () => {
    test('ALT_LINE_MODE defaults to manual', () => {
      const mode = process.env.ALT_LINE_MODE || 'manual';
      expect(mode).toBe('manual');
    });

    test('ladder generates entries at standard offsets', () => {
      const offsets = [1, 1.5, 2, 2.5, 3, 4, 5];
      const baseLine = 25.5;
      const ladder = offsets.map(offset => ({
        over_line: baseLine + offset,
        under_line: baseLine - offset,
        offset
      }));
      expect(ladder).toHaveLength(7);
      expect(ladder[0].over_line).toBe(26.5);
      expect(ladder[0].under_line).toBe(24.5);
      expect(ladder[6].over_line).toBe(30.5);
    });

    test('ladder mode returns mode=ladder in response', () => {
      const response = { eligible: true, mode: 'ladder', ladder: [] };
      expect(response.mode).toBe('ladder');
    });
  });

  // --- Item 11: GitHub Actions YAML ---

  describe('GitHub Actions Workflows', () => {
    const workflowDir = path.join(__dirname, '../../.github/workflows');

    test('nightly resolution YAML exists', () => {
      expect(fs.existsSync(path.join(workflowDir, 'vyndr-nightly.yml'))).toBe(true);
    });

    test('morning odds YAML exists', () => {
      expect(fs.existsSync(path.join(workflowDir, 'vyndr-morning-odds.yml'))).toBe(true);
    });

    test('pre-game YAML exists', () => {
      expect(fs.existsSync(path.join(workflowDir, 'vyndr-pregame.yml'))).toBe(true);
    });

    test('reporter poll YAML exists', () => {
      expect(fs.existsSync(path.join(workflowDir, 'vyndr-reporter-poll.yml'))).toBe(true);
    });

    test('weather monitor YAML exists', () => {
      expect(fs.existsSync(path.join(workflowDir, 'vyndr-weather.yml'))).toBe(true);
    });
  });

  // --- Item 12: Deployment configs ---

  describe('Deployment Configs', () => {
    test('railway.toml exists', () => {
      expect(fs.existsSync(path.join(__dirname, '../../railway.toml'))).toBe(true);
    });

    test('railway.toml has health check path', () => {
      const toml = fs.readFileSync(path.join(__dirname, '../../railway.toml'), 'utf8');
      expect(toml).toContain('healthcheckPath = "/health"');
    });

    test('railway.toml has port 5001', () => {
      const toml = fs.readFileSync(path.join(__dirname, '../../railway.toml'), 'utf8');
      expect(toml).toContain('5001');
    });
  });

  // --- Item 14: Migration 009 ---

  describe('Migration 009', () => {
    const sql = fs.readFileSync(
      path.join(__dirname, '../../supabase/migrations/009_patch_supplement.sql'), 'utf8'
    );

    test('adds columns via ALTER TABLE', () => {
      expect(sql).toContain('ALTER TABLE grade_outcomes');
    });

    test('creates unconventional_factor_data with indexes', () => {
      expect(sql).toContain('idx_ufd_factor');
      expect(sql).toContain('idx_ufd_date');
    });
  });

  // --- Item 15: MLB Coaching Helpers ---

  describe('MLB Coaching Helpers', () => {
    test('pinch hit counting from play-by-play data', () => {
      const plays = [
        { side: 'home', description: 'pinch hitter Smith', event_type: 'hit' },
        { side: 'home', description: 'regular at bat', event_type: 'hit' },
        { side: 'away', description: 'pinch hitter Jones', event_type: 'hit' },
      ];
      const count = plays.filter(p =>
        p.side === 'home' &&
        p.description.toLowerCase().includes('pinch') &&
        p.event_type.toLowerCase().includes('hit')
      ).length;
      expect(count).toBe(1);
    });

    test('sacrifice bunt counting from play-by-play data', () => {
      const plays = [
        { side: 'home', description: 'sacrifice bunt to advance runner' },
        { side: 'home', description: 'ground ball to shortstop' },
        { side: 'away', description: 'sacrifice bunt' },
      ];
      const count = plays.filter(p =>
        p.side === 'home' &&
        p.description.toLowerCase().includes('sacrifice') &&
        p.description.toLowerCase().includes('bunt')
      ).length;
      expect(count).toBe(1);
    });
  });

  // --- Item 7: MLB coaching high_leverage_hook ---

  describe('MLB Coaching Schema', () => {
    test('high_leverage_hook_tendency field exists in MLB coaching fields', () => {
      const MLB_FIELDS = [
        'starter_hook_tendency', 'quick_hook_threshold', 'bullpen_usage_philosophy',
        'intentional_walk_rate', 'pinch_hit_frequency', 'bunt_tendency',
        'save_situation_closer_only', 'platoon_tendency', 'lineup_consistency',
        'challenge_aggressiveness', 'high_leverage_hook_tendency'
      ];
      expect(MLB_FIELDS).toContain('high_leverage_hook_tendency');
      expect(MLB_FIELDS).toHaveLength(11);
    });
  });
});
