import 'dotenv/config';
import fs from 'fs';
import os from 'os';
import path from 'path';
import pool from '../db';
import { getCurrentEtDateKey } from '../lib/league-windows';
import { fetchDirectMlbPropCandidates, fetchMlbPropCandidates, type MlbPropCandidate } from '../services/mlb-prop-markets';
import { fetchTeamPropMarketCandidates, type TeamPropMarketCandidate } from '../services/team-prop-market-candidates';
import {
  buildPlayerPropSlateAuditReport,
  renderPlayerPropSlateAuditMarkdown,
  type PlayerPropSlateAuditAssetInput,
  type PlayerPropSlateAuditEventInput,
} from '../services/player-prop-slate-audit';

const args = process.argv.slice(2);

function hasFlag(flag: string): boolean {
  return args.includes(flag);
}

function getArgValue(flag: string): string | null {
  const index = args.indexOf(flag);
  if (index === -1) return null;
  return args[index + 1] || null;
}

function buildTimestampSlug(date: Date): string {
  return date.toISOString().replace(/[:.]/g, '-');
}

function buildOutputPaths(outputDir: string, auditedAt: Date): { jsonPath: string; markdownPath: string } {
  const slug = buildTimestampSlug(auditedAt);
  return {
    jsonPath: path.join(outputDir, `player-prop-slate-audit-${slug}.json`),
    markdownPath: path.join(outputDir, `player-prop-slate-audit-${slug}.md`),
  };
}

async function fetchAuditEvents(dateEt: string, leagueFilter: string | null): Promise<PlayerPropSlateAuditEventInput[]> {
  const params: any[] = [dateEt];
  const where = [`(e.starts_at AT TIME ZONE 'America/New_York')::date = $1::date`];

  if (leagueFilter) {
    params.push(leagueFilter.toLowerCase());
    where.push(`LOWER(e.league) = $${params.length}`);
  }

  const { rows } = await pool.query(
    `
      SELECT
        e.event_id,
        e.league,
        CONCAT(COALESCE(e.away_short, e.away_team), ' @ ', COALESCE(e.home_short, e.home_team)) AS matchup,
        e.starts_at,
        e.home_short,
        e.away_short,
        e.home_team,
        e.away_team,
        (fc.event_id IS NOT NULL) AS has_forecast,
        COALESCE(jsonb_array_length(fc.forecast_data->'prop_highlights'), 0) AS prop_highlights_count
      FROM rm_events e
      LEFT JOIN rm_forecast_cache fc ON fc.event_id = e.event_id
      WHERE ${where.join(' AND ')}
      ORDER BY e.starts_at ASC
    `,
    params,
  );

  const events = rows.map((row: any) => ({
    eventId: row.event_id,
    league: row.league,
    matchup: row.matchup,
    startsAt: row.starts_at,
    hasForecast: Boolean(row.has_forecast),
    propHighlightsCount: Number(row.prop_highlights_count || 0),
    homeShort: row.home_short || null,
    awayShort: row.away_short || null,
    homeTeam: row.home_team || null,
    awayTeam: row.away_team || null,
  }));

  const sourceBackedCoverage = new Map<string, { count: number; source: 'local' | 'direct' | 'candidates' | null }>();
  for (const event of events) {
    const startsAt = event.startsAt instanceof Date ? event.startsAt.toISOString() : event.startsAt ? new Date(event.startsAt).toISOString() : null;
    const league = String(event.league || '').toLowerCase();
    const homeShort = String((event as any).homeShort || '').trim().toUpperCase();
    const awayShort = String((event as any).awayShort || '').trim().toUpperCase();
    if (!startsAt || !homeShort || !awayShort) continue;

    if (league !== 'mlb') {
      const [homeCandidates, awayCandidates] = await Promise.all([
        fetchTeamPropMarketCandidates({
          league,
          teamShort: homeShort,
          opponentShort: awayShort,
          teamName: String((event as any).homeTeam || homeShort),
          opponentName: String((event as any).awayTeam || awayShort),
          homeTeam: String((event as any).homeTeam || homeShort),
          awayTeam: String((event as any).awayTeam || awayShort),
          startsAt,
          skipTheOddsVerification: true,
        }).catch(() => [] as TeamPropMarketCandidate[]),
        fetchTeamPropMarketCandidates({
          league,
          teamShort: awayShort,
          opponentShort: homeShort,
          teamName: String((event as any).awayTeam || awayShort),
          opponentName: String((event as any).homeTeam || homeShort),
          homeTeam: String((event as any).homeTeam || homeShort),
          awayTeam: String((event as any).awayTeam || awayShort),
          startsAt,
          skipTheOddsVerification: true,
        }).catch(() => [] as TeamPropMarketCandidate[]),
      ]);

      const candidateCount = countSourceCandidateRows(homeCandidates) + countSourceCandidateRows(awayCandidates);
      if (candidateCount > 0) {
        sourceBackedCoverage.set(event.eventId, { count: candidateCount, source: 'candidates' });
      }
      continue;
    }

    const homeLocal = await fetchMlbPropCandidates({
      teamShort: homeShort,
      teamName: String((event as any).homeTeam || homeShort),
      opponentShort: awayShort,
      startsAt,
    }).catch(() => [] as MlbPropCandidate[]);
    const awayLocal = await fetchMlbPropCandidates({
      teamShort: awayShort,
      teamName: String((event as any).awayTeam || awayShort),
      opponentShort: homeShort,
      startsAt,
    }).catch(() => [] as MlbPropCandidate[]);

    const localCount = countCandidateRows(homeLocal) + countCandidateRows(awayLocal);
    if (localCount > 0) {
      sourceBackedCoverage.set(event.eventId, { count: localCount, source: 'local' });
      continue;
    }

    const homeDirect = await fetchDirectMlbPropCandidates({
      teamShort: homeShort,
      teamName: String((event as any).homeTeam || homeShort),
      opponentShort: awayShort,
      startsAt,
    }).catch(() => [] as MlbPropCandidate[]);
    const awayDirect = await fetchDirectMlbPropCandidates({
      teamShort: awayShort,
      teamName: String((event as any).awayTeam || awayShort),
      opponentShort: homeShort,
      startsAt,
    }).catch(() => [] as MlbPropCandidate[]);
    sourceBackedCoverage.set(event.eventId, {
      count: countCandidateRows(homeDirect) + countCandidateRows(awayDirect),
      source: 'direct',
    });
  }

  return events.map((event: any) => {
    const sourceBacked = sourceBackedCoverage.get(event.eventId);
    return {
      eventId: event.eventId,
      league: event.league,
      matchup: event.matchup,
      startsAt: event.startsAt,
      hasForecast: event.hasForecast,
      propHighlightsCount: event.propHighlightsCount,
      sourceBackedPlayerPropsCount: sourceBacked?.count || 0,
      sourceBackedSource: sourceBacked?.count ? sourceBacked.source : null,
      liveFallbackPlayerPropsCount: sourceBacked?.count || 0,
      liveFallbackSource: sourceBacked?.count ? sourceBacked.source : null,
    };
  });
}

function countCandidateRows(candidates: MlbPropCandidate[]): number {
  return candidates.reduce((sum, candidate) => {
    const sides = Array.isArray(candidate.availableSides) ? candidate.availableSides : [];
    for (const side of sides) {
      if (side === 'over' && candidate.overOdds != null) sum += 1;
      if (side === 'under' && candidate.underOdds != null) sum += 1;
    }
    return sum;
  }, 0);
}

function countSourceCandidateRows(candidates: TeamPropMarketCandidate[]): number {
  return candidates.reduce((sum, candidate) => {
    const sides = Array.isArray(candidate.availableSides) ? candidate.availableSides : [];
    for (const side of sides) {
      if (side === 'over' && candidate.overOdds != null) sum += 1;
      if (side === 'under' && candidate.underOdds != null) sum += 1;
    }
    return sum;
  }, 0);
}

async function fetchAuditAssets(dateEt: string, leagueFilter: string | null): Promise<PlayerPropSlateAuditAssetInput[]> {
  const params: any[] = [dateEt];
  const where = ['date_et = $1'];

  if (leagueFilter) {
    params.push(leagueFilter.toLowerCase());
    where.push(`LOWER(league) = $${params.length}`);
  }

  const { rows } = await pool.query(
    `
      SELECT
        event_id,
        league,
        forecast_type,
        status,
        player_name,
        confidence_score,
        forecast_payload
      FROM rm_forecast_precomputed
      WHERE ${where.join(' AND ')}
    `,
    params,
  );

  return rows.map((row: any) => ({
    eventId: row.event_id,
    league: row.league,
    forecastType: row.forecast_type,
    status: row.status,
    playerName: row.player_name,
    confidenceScore: row.confidence_score == null ? null : Number(row.confidence_score),
    payload: row.forecast_payload || null,
  }));
}

async function main() {
  const auditedAt = new Date();
  const dateEt = getArgValue('--date') || getCurrentEtDateKey(auditedAt);
  const leagueFilter = getArgValue('--league');
  const outputDir = getArgValue('--output-dir') || path.join(os.tmpdir(), 'rainmaker-prop-audits');
  const failOnFindings = !hasFlag('--no-fail-on-findings');

  const [events, assets] = await Promise.all([
    fetchAuditEvents(dateEt, leagueFilter),
    fetchAuditAssets(dateEt, leagueFilter),
  ]);

  const report = buildPlayerPropSlateAuditReport({
    auditedAt: auditedAt.toISOString(),
    dateEt,
    events,
    assets,
  });

  fs.mkdirSync(outputDir, { recursive: true });
  const { jsonPath, markdownPath } = buildOutputPaths(outputDir, auditedAt);
  fs.writeFileSync(jsonPath, JSON.stringify(report, null, 2));
  fs.writeFileSync(markdownPath, renderPlayerPropSlateAuditMarkdown(report));

  console.log(JSON.stringify({
    auditedAt: report.auditedAt,
    dateEt,
    scope: leagueFilter ? `league:${leagueFilter.toLowerCase()}` : `date_et:${dateEt}`,
    passed: report.passed,
    summary: report.summary,
    jsonPath,
    markdownPath,
  }, null, 2));

  await pool.end();
  if (failOnFindings && !report.passed) {
    process.exit(1);
  }
}

if (require.main === module) {
  main().catch(async (err) => {
    console.error('[player-prop-slate-audit] Fatal:', err);
    await pool.end().catch(() => {});
    process.exit(1);
  });
}
