import Decimal from 'decimal.js';

export type SelectionResult = 'WIN' | 'HALF_WIN' | 'PUSH' | 'HALF_LOSE' | 'LOSE' | 'VOID';

export interface ScoreInput {
  htHome: number;
  htAway: number;
  ftHome: number;
  ftAway: number;
}

export interface MatchStatsInput {
  homeCorners?: number | null;
  awayCorners?: number | null;
  homeYellowCards?: number | null;
  awayYellowCards?: number | null;
  homeRedCards?: number | null;
  awayRedCards?: number | null;
  homeCards?: number | null;
  awayCards?: number | null;
}

export interface SettlementInput {
  marketType: string;
  selectionCode: string;
  handicapLine?: number | null;
  totalLine?: number | null;
  score: ScoreInput;
  stats?: MatchStatsInput | null;
  templateScores?: string[];
  /** 冠军盘：获胜球队 code，如 FRA、BRA */
  winnerTeamCode?: string | null;
}

export function getShScore(score: ScoreInput): { home: number; away: number } {
  return {
    home: score.ftHome - score.htHome,
    away: score.ftAway - score.htAway,
  };
}

function isQuarterLine(line: number): boolean {
  const frac = Math.abs(line % 1);
  return Math.abs(frac - 0.25) < 0.001 || Math.abs(frac - 0.75) < 0.001;
}

function splitQuarterLine(line: number): [number, number] {
  const sign = line >= 0 ? 1 : -1;
  const abs = Math.abs(line);
  const lower = Math.floor(abs * 2) / 2 * sign;
  const upper = (Math.floor(abs * 2) + 1) / 2 * sign;
  if (Math.abs(abs % 1 - 0.25) < 0.001) {
    return [lower, upper];
  }
  // 0.75 case: e.g. -0.75 => -0.5 and -1
  const l = Math.floor(abs) * sign;
  const u = (Math.floor(abs) + 0.5) * sign;
  return abs % 1 > 0.5 ? [l, u] : [lower, upper];
}

function settleHandicap(
  teamGoals: number,
  oppGoals: number,
  handicap: number,
  isHome: boolean,
): SelectionResult {
  const adj = teamGoals + handicap - oppGoals;

  if (!isQuarterLine(handicap)) {
    if (adj > 0) return 'WIN';
    if (adj === 0) return 'PUSH';
    return 'LOSE';
  }

  const [line1, line2] = splitQuarterLine(handicap);
  const r1 = teamGoals + line1 - oppGoals;
  const r2 = teamGoals + line2 - oppGoals;

  const results: SelectionResult[] = [];
  for (const r of [r1, r2]) {
    if (r > 0) results.push('WIN');
    else if (r === 0) results.push('PUSH');
    else results.push('LOSE');
  }

  const winCount = results.filter((r) => r === 'WIN').length;
  const loseCount = results.filter((r) => r === 'LOSE').length;

  if (winCount === 2) return 'WIN';
  if (loseCount === 2) return 'LOSE';
  if (winCount === 1 && loseCount === 0) return 'HALF_WIN';
  if (loseCount === 1 && winCount === 0) return 'HALF_LOSE';
  return 'PUSH';
}

function settleOverUnder(
  totalGoals: number,
  line: number,
  isOver: boolean,
): SelectionResult {
  if (!isQuarterLine(line)) {
    if (isOver) {
      if (totalGoals > line) return 'WIN';
      if (totalGoals === line) return 'PUSH';
      return 'LOSE';
    }
    if (totalGoals < line) return 'WIN';
    if (totalGoals === line) return 'PUSH';
    return 'LOSE';
  }

  const [line1, line2] = splitQuarterLine(line);
  const r1 = settleOverUnder(totalGoals, line1, isOver);
  const r2 = settleOverUnder(totalGoals, line2, isOver);

  const winCount = [r1, r2].filter((r) => r === 'WIN').length;
  const loseCount = [r1, r2].filter((r) => r === 'LOSE').length;

  if (winCount === 2) return 'WIN';
  if (loseCount === 2) return 'LOSE';
  if (winCount === 1 && loseCount === 0) return 'HALF_WIN';
  if (loseCount === 1 && winCount === 0) return 'HALF_LOSE';
  return 'PUSH';
}

function parseScoreCode(code: string): { home: number; away: number } | null {
  const match = code.match(/SCORE_(\d+)_(\d+)/);
  if (match) return { home: parseInt(match[1]), away: parseInt(match[2]) };
  return null;
}

function settleCorrectScore(
  home: number,
  away: number,
  selectionCode: string,
  templateScores: string[],
): SelectionResult {
  const parsed = parseScoreCode(selectionCode);
  if (parsed) {
    return parsed.home === home && parsed.away === away ? 'WIN' : 'LOSE';
  }

  const actualKey = `SCORE_${home}_${away}`;
  if (templateScores.includes(actualKey)) {
    return 'LOSE';
  }

  const isDraw = home === away;
  const isHomeWin = home > away;

  if (selectionCode === 'OTHER_DRAW' && isDraw) return 'WIN';
  if (selectionCode === 'OTHER_HOME' && isHomeWin) return 'WIN';
  if (selectionCode === 'OTHER_AWAY' && !isHomeWin && !isDraw) return 'WIN';

  return 'LOSE';
}

function resultSide(home: number, away: number): 'HOME' | 'DRAW' | 'AWAY' {
  if (home > away) return 'HOME';
  if (home < away) return 'AWAY';
  return 'DRAW';
}

function settleHtFt(score: ScoreInput, selectionCode: string): SelectionResult {
  const expected = `${resultSide(score.htHome, score.htAway)}_${resultSide(score.ftHome, score.ftAway)}`;
  return selectionCode === expected ? 'WIN' : 'LOSE';
}

function settleTotalGoalsRange(totalGoals: number, selectionCode: string): SelectionResult {
  if (selectionCode === 'TG_0_1') return totalGoals <= 1 ? 'WIN' : 'LOSE';
  if (selectionCode === 'TG_2_3') return totalGoals >= 2 && totalGoals <= 3 ? 'WIN' : 'LOSE';
  if (selectionCode === 'TG_4_6') return totalGoals >= 4 && totalGoals <= 6 ? 'WIN' : 'LOSE';
  if (selectionCode === 'TG_7_PLUS') return totalGoals >= 7 ? 'WIN' : 'LOSE';

  const exact = selectionCode.match(/^GOALS_(\d+)$/);
  if (exact) return totalGoals === Number(exact[1]) ? 'WIN' : 'LOSE';
  const exactPlus = selectionCode.match(/^GOALS_(\d+)_PLUS$/);
  if (exactPlus) return totalGoals >= Number(exactPlus[1]) ? 'WIN' : 'LOSE';

  return 'LOSE';
}

function requireStat(value: number | null | undefined, field: keyof MatchStatsInput): number {
  if (value == null || Number.isNaN(value)) {
    throw new Error(`SETTLEMENT_STAT_MISSING:${field}`);
  }
  return value;
}

export function settleSelection(input: SettlementInput): SelectionResult {
  const { marketType, selectionCode, handicapLine, totalLine, score } = input;
  const stats = input.stats ?? {};
  const templates = input.templateScores ?? [];

  switch (marketType) {
    case 'FT_1X2': {
      if (selectionCode === 'HOME') return score.ftHome > score.ftAway ? 'WIN' : 'LOSE';
      if (selectionCode === 'DRAW') return score.ftHome === score.ftAway ? 'WIN' : 'LOSE';
      if (selectionCode === 'AWAY') return score.ftHome < score.ftAway ? 'WIN' : 'LOSE';
      break;
    }
    case 'HT_1X2': {
      if (selectionCode === 'HOME') return score.htHome > score.htAway ? 'WIN' : 'LOSE';
      if (selectionCode === 'DRAW') return score.htHome === score.htAway ? 'WIN' : 'LOSE';
      if (selectionCode === 'AWAY') return score.htHome < score.htAway ? 'WIN' : 'LOSE';
      break;
    }
    case 'FT_ODD_EVEN': {
      const total = score.ftHome + score.ftAway;
      const isOdd = total % 2 === 1;
      if (selectionCode === 'ODD') return isOdd ? 'WIN' : 'LOSE';
      if (selectionCode === 'EVEN') return !isOdd ? 'WIN' : 'LOSE';
      break;
    }
    case 'FT_HANDICAP': {
      const line = handicapLine ?? 0;
      const isHome = selectionCode === 'HOME';
      const goals = isHome ? score.ftHome : score.ftAway;
      const opp = isHome ? score.ftAway : score.ftHome;
      return settleHandicap(goals, opp, isHome ? line : -line, isHome);
    }
    case 'HT_HANDICAP': {
      const line = handicapLine ?? 0;
      const isHome = selectionCode === 'HOME';
      const goals = isHome ? score.htHome : score.htAway;
      const opp = isHome ? score.htAway : score.htHome;
      return settleHandicap(goals, opp, isHome ? line : -line, isHome);
    }
    case 'FT_OVER_UNDER': {
      const total = score.ftHome + score.ftAway;
      return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
    }
    case 'HT_OVER_UNDER': {
      const total = score.htHome + score.htAway;
      return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
    }
    case 'FT_TEAM_TOTAL_HOME':
      return settleOverUnder(score.ftHome, totalLine ?? 0, selectionCode === 'OVER');
    case 'FT_TEAM_TOTAL_AWAY':
      return settleOverUnder(score.ftAway, totalLine ?? 0, selectionCode === 'OVER');
    case 'HT_FT':
      return settleHtFt(score, selectionCode);
    case 'FT_TOTAL_GOALS':
      return settleTotalGoalsRange(score.ftHome + score.ftAway, selectionCode);
    case 'FT_CORNERS_HANDICAP': {
      const homeCorners = requireStat(stats.homeCorners, 'homeCorners');
      const awayCorners = requireStat(stats.awayCorners, 'awayCorners');
      const line = handicapLine ?? 0;
      const isHome = selectionCode === 'HOME';
      const corners = isHome ? homeCorners : awayCorners;
      const opp = isHome ? awayCorners : homeCorners;
      return settleHandicap(corners, opp, isHome ? line : -line, isHome);
    }
    case 'FT_CORNERS_OVER_UNDER': {
      const total =
        requireStat(stats.homeCorners, 'homeCorners') +
        requireStat(stats.awayCorners, 'awayCorners');
      return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
    }
    case 'FT_CARDS_OVER_UNDER': {
      const total =
        requireStat(stats.homeCards, 'homeCards') +
        requireStat(stats.awayCards, 'awayCards');
      return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
    }
    case 'FT_CORRECT_SCORE':
      return settleCorrectScore(score.ftHome, score.ftAway, selectionCode, templates);
    case 'HT_CORRECT_SCORE':
      return settleCorrectScore(score.htHome, score.htAway, selectionCode, templates);
    case 'SH_CORRECT_SCORE': {
      const sh = getShScore(score);
      return settleCorrectScore(sh.home, sh.away, selectionCode, templates);
    }
    case 'OUTRIGHT_WINNER':
      if (!input.winnerTeamCode) return 'LOSE';
      return selectionCode === input.winnerTeamCode ? 'WIN' : 'LOSE';
  }

  return 'LOSE';
}

export function calculatePayout(
  stake: Decimal | number,
  odds: Decimal | number,
  result: SelectionResult,
): Decimal {
  const s = new Decimal(stake);
  const o = new Decimal(odds);

  switch (result) {
    case 'WIN':
      return s.mul(o);
    case 'HALF_WIN':
      return s.div(2).mul(o).add(s.div(2));
    case 'PUSH':
    case 'VOID':
      return s;
    case 'HALF_LOSE':
      return s.div(2);
    case 'LOSE':
      return new Decimal(0);
    default:
      return new Decimal(0);
  }
}

export function calculateParlayPayout(
  stake: Decimal | number,
  selections: Array<{ odds: Decimal | number; result: SelectionResult }>,
): { betResult: 'WON' | 'LOST' | 'PUSH'; payout: Decimal; effectiveOdds: Decimal } {
  const s = new Decimal(stake);

  if (selections.some((sel) => sel.result === 'LOSE')) {
    return { betResult: 'LOST', payout: new Decimal(0), effectiveOdds: new Decimal(0) };
  }

  let combinedOdds = new Decimal(1);
  for (const sel of selections) {
    if (sel.result === 'WIN') {
      combinedOdds = combinedOdds.mul(sel.odds);
    } else if (sel.result === 'HALF_WIN') {
      combinedOdds = combinedOdds.mul(new Decimal(sel.odds).add(1).div(2));
    } else if (sel.result === 'HALF_LOSE') {
      combinedOdds = combinedOdds.mul(0.5);
    }
    // PUSH/VOID => odds 1.00
  }

  const allPush = selections.every(
    (sel) => sel.result === 'PUSH' || sel.result === 'VOID',
  );
  if (allPush) {
    return { betResult: 'PUSH', payout: s, effectiveOdds: new Decimal(1) };
  }

  return { betResult: 'WON', payout: s.mul(combinedOdds), effectiveOdds: combinedOdds };
}

export {
  FT_CORRECT_SCORE_TEMPLATE,
  HT_CORRECT_SCORE_TEMPLATE,
  isQuarterHandicapOrTotal,
} from '@thebet365/shared';
