import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../shared/prisma/prisma.service';
import { Decimal } from '@prisma/client/runtime/library';
import {
  FT_CORRECT_SCORE_TEMPLATE,
  HT_CORRECT_SCORE_TEMPLATE,
} from '../settlement/domain/settlement-calculator';

@Injectable()
export class MarketsService {
  constructor(private prisma: PrismaService) {}

  async generateTemplates(matchId: bigint, marketTypes: string[]) {
    const match = await this.prisma.match.findUnique({ where: { id: matchId } });
    if (!match) throw new NotFoundException('Match not found');

    const created = [];

    for (const marketType of marketTypes) {
      const existing = await this.prisma.market.findFirst({
        where: { matchId, marketType },
      });
      if (existing) continue;

      const config = this.getMarketConfig(marketType);
      const market = await this.prisma.market.create({
        data: {
          matchId,
          marketType,
          period: config.period,
          lineValue: config.lineValue,
          allowSingle: true,
          allowParlay: config.allowParlay,
          sortOrder: config.sortOrder,
          selections: {
            create: config.selections.map((s, i) => ({
              selectionCode: s.code,
              selectionName: s.name,
              odds: s.odds ?? 1.01,
              sortOrder: i,
            })),
          },
        },
        include: { selections: true },
      });
      created.push(market);
    }

    return created;
  }

  private formatHandicapName(side: 'home' | 'away', line: number, half = false) {
    const sideLabel = side === 'home' ? '主队' : '客队';
    const value = side === 'home' ? line : -line;
    const lineText = value > 0 ? `+${value}` : `${value}`;
    return half ? `半场${sideLabel} ${lineText}` : `${sideLabel} ${lineText}`;
  }

  private formatOuName(side: 'over' | 'under', line: number, half = false) {
    const sideLabel = side === 'over' ? '大' : '小';
    return half ? `半场${sideLabel} ${line}` : `${sideLabel} ${line}`;
  }

  private formatScoreName(code: string) {
    return code.replace('SCORE_', '').replace('_', '-');
  }

  private getMarketConfig(marketType: string) {
    const configs: Record<string, {
      period: string;
      lineValue?: number;
      allowParlay: boolean;
      sortOrder: number;
      selections: Array<{ code: string; name: string; odds?: number }>;
    }> = {
      FT_1X2: {
        period: 'FT',
        allowParlay: true,
        sortOrder: 1,
        selections: [
          { code: 'HOME', name: '主胜', odds: 2.5 },
          { code: 'DRAW', name: '和', odds: 3.2 },
          { code: 'AWAY', name: '客胜', odds: 2.8 },
        ],
      },
      HT_1X2: {
        period: 'HT',
        allowParlay: true,
        sortOrder: 5,
        selections: [
          { code: 'HOME', name: '半场主胜', odds: 3.0 },
          { code: 'DRAW', name: '半场和', odds: 2.0 },
          { code: 'AWAY', name: '半场客胜', odds: 3.5 },
        ],
      },
      FT_HANDICAP: {
        period: 'FT',
        lineValue: -0.5,
        allowParlay: true,
        sortOrder: 2,
        selections: [
          { code: 'HOME', name: this.formatHandicapName('home', -0.5), odds: 1.9 },
          { code: 'AWAY', name: this.formatHandicapName('away', -0.5), odds: 1.9 },
        ],
      },
      HT_HANDICAP: {
        period: 'HT',
        lineValue: -0.5,
        allowParlay: true,
        sortOrder: 6,
        selections: [
          { code: 'HOME', name: this.formatHandicapName('home', -0.5, true), odds: 1.9 },
          { code: 'AWAY', name: this.formatHandicapName('away', -0.5, true), odds: 1.9 },
        ],
      },
      FT_OVER_UNDER: {
        period: 'FT',
        lineValue: 2.5,
        allowParlay: true,
        sortOrder: 3,
        selections: [
          { code: 'OVER', name: this.formatOuName('over', 2.5), odds: 1.85 },
          { code: 'UNDER', name: this.formatOuName('under', 2.5), odds: 1.95 },
        ],
      },
      HT_OVER_UNDER: {
        period: 'HT',
        lineValue: 1.5,
        allowParlay: true,
        sortOrder: 7,
        selections: [
          { code: 'OVER', name: this.formatOuName('over', 1.5, true), odds: 2.0 },
          { code: 'UNDER', name: this.formatOuName('under', 1.5, true), odds: 1.75 },
        ],
      },
      FT_ODD_EVEN: {
        period: 'FT',
        allowParlay: true,
        sortOrder: 4,
        selections: [
          { code: 'ODD', name: '单', odds: 1.9 },
          { code: 'EVEN', name: '双', odds: 1.9 },
        ],
      },
      FT_CORRECT_SCORE: {
        period: 'FT',
        allowParlay: true,
        sortOrder: 8,
        selections: FT_CORRECT_SCORE_TEMPLATE.map((code) => ({
          code,
          name: this.formatScoreName(code),
          odds: 8.0,
        })),
      },
      HT_CORRECT_SCORE: {
        period: 'HT',
        allowParlay: true,
        sortOrder: 9,
        selections: HT_CORRECT_SCORE_TEMPLATE.map((code) => ({
          code,
          name: this.formatScoreName(code),
          odds: 6.0,
        })),
      },
      SH_CORRECT_SCORE: {
        period: 'SH',
        allowParlay: true,
        sortOrder: 10,
        selections: HT_CORRECT_SCORE_TEMPLATE.map((code) => ({
          code,
          name: this.formatScoreName(code),
          odds: 6.0,
        })),
      },
      OUTRIGHT_WINNER: {
        period: 'OUTRIGHT',
        allowParlay: false,
        sortOrder: 1,
        selections: [],
      },
    };

    const config = configs[marketType];
    if (!config) throw new BadRequestException(`Unknown market type: ${marketType}`);
    return config;
  }

  async updateOdds(selectionId: bigint, newOdds: number, operatorId: bigint) {
    const selection = await this.prisma.marketSelection.findUnique({
      where: { id: selectionId },
    });
    if (!selection) throw new NotFoundException('Selection not found');
    if (newOdds <= 1) throw new BadRequestException('Odds must be > 1.00');

    const newVersion = selection.oddsVersion + BigInt(1);

    return this.prisma.$transaction(async (tx) => {
      await tx.oddsChangeLog.create({
        data: {
          selectionId,
          oldOdds: selection.odds,
          newOdds,
          oddsVersion: newVersion,
          changedBy: operatorId,
        },
      });

      return tx.marketSelection.update({
        where: { id: selectionId },
        data: { odds: newOdds, oddsVersion: newVersion },
      });
    });
  }

  async batchUpdateOdds(
    updates: Array<{ selectionId: bigint; odds: number }>,
    operatorId: bigint,
  ) {
    const results = [];
    for (const u of updates) {
      results.push(await this.updateOdds(u.selectionId, u.odds, operatorId));
    }
    return results;
  }

  async updateMarket(
    marketId: bigint,
    data: { promoLabel?: string | null; status?: string; lineValue?: number | null },
  ) {
    const market = await this.prisma.market.findUnique({ where: { id: marketId } });
    if (!market) throw new NotFoundException('Market not found');

    return this.prisma.market.update({
      where: { id: marketId },
      data: {
        ...(data.promoLabel !== undefined ? { promoLabel: data.promoLabel?.trim() || null } : {}),
        ...(data.status !== undefined ? { status: data.status } : {}),
        ...(data.lineValue !== undefined ? { lineValue: data.lineValue } : {}),
      },
    });
  }

  async updateSelection(
    selectionId: bigint,
    data: { selectionName?: string; odds?: number; status?: string },
    operatorId?: bigint,
  ) {
    const selection = await this.prisma.marketSelection.findUnique({
      where: { id: selectionId },
    });
    if (!selection) throw new NotFoundException('Selection not found');

    if (data.odds != null) {
      if (!operatorId) throw new BadRequestException('Operator required for odds update');
      return this.updateOdds(selectionId, data.odds, operatorId);
    }

    return this.prisma.marketSelection.update({
      where: { id: selectionId },
      data: {
        ...(data.selectionName !== undefined ? { selectionName: data.selectionName.trim() } : {}),
        ...(data.status !== undefined ? { status: data.status } : {}),
      },
    });
  }
}
