import { NextRequest, NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { db } from '@/lib/db';

interface RouteContext {
  params: Promise<{ id: string }>;
}

interface Team {
  id: string;
  clientId: string;
  partnerId: string | null;
  teamName: string | null;
  seedNumber: number | null;
  client: {
    id: string;
    firstName: string;
    lastName: string;
  };
}

interface MatchData {
  tournamentId: string;
  round: number;
  position: number;
  status: 'SCHEDULED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED' | 'WALKOVER';
  team1Players: string[];
  team2Players: string[];
}

// Calculate next power of 2 that is >= n
function nextPowerOf2(n: number): number {
  let power = 1;
  while (power < n) {
    power *= 2;
  }
  return power;
}

// Calculate number of rounds for single elimination
function calculateRounds(teamCount: number): number {
  return Math.ceil(Math.log2(teamCount));
}

// Generate single elimination bracket
function generateSingleEliminationBracket(
  tournamentId: string,
  teams: Team[]
): MatchData[] {
  const teamCount = teams.length;
  const bracketSize = nextPowerOf2(teamCount);
  const totalRounds = calculateRounds(bracketSize);
  const byeCount = bracketSize - teamCount;

  const matches: MatchData[] = [];

  // Sort teams by seed number (if available) for proper seeding
  const sortedTeams = [...teams].sort((a, b) => {
    if (a.seedNumber === null && b.seedNumber === null) return 0;
    if (a.seedNumber === null) return 1;
    if (b.seedNumber === null) return -1;
    return a.seedNumber - b.seedNumber;
  });

  // Create seeding positions for standard bracket seeding
  // For 8 teams: [1,8,4,5,2,7,3,6] - this ensures top seeds meet later
  const seedingOrder = createSeedingOrder(bracketSize);

  // Map teams to their seeding positions
  const teamSlots: (Team | null)[] = new Array(bracketSize).fill(null);

  for (let i = 0; i < sortedTeams.length; i++) {
    const position = seedingOrder[i] - 1; // Convert to 0-indexed
    teamSlots[position] = sortedTeams[i];
  }

  // First round matches (round 1)
  const firstRoundMatchCount = bracketSize / 2;

  for (let pos = 0; pos < firstRoundMatchCount; pos++) {
    const team1Index = pos * 2;
    const team2Index = pos * 2 + 1;
    const team1 = teamSlots[team1Index];
    const team2 = teamSlots[team2Index];

    const match: MatchData = {
      tournamentId,
      round: 1,
      position: pos + 1,
      status: 'SCHEDULED',
      team1Players: team1 ? getTeamPlayerIds(team1) : [],
      team2Players: team2 ? getTeamPlayerIds(team2) : [],
    };

    // If one team is a bye (null), mark as walkover and advance the other team
    if (!team1 && team2) {
      match.status = 'WALKOVER';
      match.team1Players = []; // Bye
    } else if (team1 && !team2) {
      match.status = 'WALKOVER';
      match.team2Players = []; // Bye
    } else if (!team1 && !team2) {
      // Both are byes - this shouldn't happen with proper seeding
      match.status = 'WALKOVER';
    }

    matches.push(match);
  }

  // Create subsequent round matches (empty, to be filled as tournament progresses)
  for (let round = 2; round <= totalRounds; round++) {
    const matchesInRound = bracketSize / Math.pow(2, round);

    for (let pos = 0; pos < matchesInRound; pos++) {
      matches.push({
        tournamentId,
        round,
        position: pos + 1,
        status: 'SCHEDULED',
        team1Players: [],
        team2Players: [],
      });
    }
  }

  return matches;
}

// Get player IDs for a team (client + partner if exists)
function getTeamPlayerIds(team: Team): string[] {
  const ids = [team.clientId];
  if (team.partnerId) {
    ids.push(team.partnerId);
  }
  return ids;
}

// Create standard bracket seeding order
// For power of 2 brackets, ensures that #1 seed meets #2 seed only in finals
function createSeedingOrder(size: number): number[] {
  if (size === 2) return [1, 2];

  const order: number[] = [];
  const halfSize = size / 2;
  const prevOrder = createSeedingOrder(halfSize);

  for (let i = 0; i < halfSize; i++) {
    order.push(prevOrder[i]);
    order.push(size + 1 - prevOrder[i]);
  }

  return order;
}

// Process walkover matches and advance winners
async function processWalkovers(
  tx: Omit<typeof db, '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'>,
  matches: { id: string; round: number; position: number; status: string; team1Players: string[]; team2Players: string[] }[]
): Promise<void> {
  const walkovers = matches.filter(m => m.status === 'WALKOVER');

  for (const match of walkovers) {
    // Determine winner (the team that's not empty)
    const winner = match.team1Players.length > 0 ? match.team1Players : match.team2Players;

    if (winner.length === 0) continue; // Both empty, no winner to advance

    // Find the next round match this winner advances to
    const nextRound = match.round + 1;
    const nextPosition = Math.ceil(match.position / 2);

    const nextMatch = matches.find(m => m.round === nextRound && m.position === nextPosition);
    if (!nextMatch) continue; // Final match or not found

    // Determine if this match feeds into team1 or team2 of next match
    const isUpperBracket = match.position % 2 === 1;

    if (isUpperBracket) {
      await tx.match.update({
        where: { id: nextMatch.id },
        data: { team1Players: winner },
      });
    } else {
      await tx.match.update({
        where: { id: nextMatch.id },
        data: { team2Players: winner },
      });
    }

    // Update the walkover match with winnerId
    await tx.match.update({
      where: { id: match.id },
      data: {
        winnerId: winner[0], // Use first player as winner identifier
        completedAt: new Date(),
        status: 'COMPLETED',
      },
    });
  }
}

// POST /api/tournaments/[id]/generate-bracket - Generate bracket/matches
export async function POST(
  request: NextRequest,
  context: RouteContext
) {
  try {
    const session = await getServerSession(authOptions);

    if (!session?.user) {
      return NextResponse.json(
        { error: 'Unauthorized' },
        { status: 401 }
      );
    }

    // Check if user has admin role
    const allowedRoles = ['SUPER_ADMIN', 'ORG_ADMIN', 'SITE_ADMIN'];
    if (!allowedRoles.includes(session.user.role)) {
      return NextResponse.json(
        { error: 'Forbidden: Insufficient permissions' },
        { status: 403 }
      );
    }

    const { id } = await context.params;

    // Verify tournament exists and belongs to user's organization
    const tournament = await db.tournament.findFirst({
      where: {
        id,
        organizationId: session.user.organizationId,
      },
      include: {
        inscriptions: {
          include: {
            client: {
              select: {
                id: true,
                firstName: true,
                lastName: true,
              },
            },
          },
        },
        matches: true,
      },
    });

    if (!tournament) {
      return NextResponse.json(
        { error: 'Tournament not found' },
        { status: 404 }
      );
    }

    // Check tournament status is REGISTRATION_OPEN
    if (tournament.status !== 'REGISTRATION_OPEN') {
      return NextResponse.json(
        { error: 'Tournament must be in REGISTRATION_OPEN status to generate bracket. Current status: ' + tournament.status },
        { status: 400 }
      );
    }

    // Check minimum inscriptions (at least 2 teams)
    if (tournament.inscriptions.length < 2) {
      return NextResponse.json(
        { error: 'At least 2 teams must be registered to generate bracket' },
        { status: 400 }
      );
    }

    // Check if bracket already exists
    if (tournament.matches.length > 0) {
      return NextResponse.json(
        { error: 'Bracket already exists. Delete existing matches first if you want to regenerate.' },
        { status: 400 }
      );
    }

    // Get tournament format from settings
    const settings = tournament.settings as { tournamentFormat?: string } | null;
    const format = settings?.tournamentFormat || 'SINGLE_ELIMINATION';

    // Currently only supporting single elimination
    if (format !== 'SINGLE_ELIMINATION' && format !== 'BRACKET') {
      return NextResponse.json(
        { error: `Bracket generation for ${format} format is not yet supported. Only SINGLE_ELIMINATION is currently available.` },
        { status: 400 }
      );
    }

    // Generate bracket matches
    const matchData = generateSingleEliminationBracket(
      id,
      tournament.inscriptions as Team[]
    );

    // Create matches in transaction
    const result = await db.$transaction(async (tx) => {
      // Create all matches
      const createdMatches = [];
      for (const match of matchData) {
        const created = await tx.match.create({
          data: match,
        });
        createdMatches.push({
          ...created,
          status: match.status,
        });
      }

      // Process walkovers and advance winners
      await processWalkovers(tx, createdMatches as { id: string; round: number; position: number; status: string; team1Players: string[]; team2Players: string[] }[]);

      // Update tournament status to IN_PROGRESS
      await tx.tournament.update({
        where: { id },
        data: { status: 'IN_PROGRESS' },
      });

      // Fetch updated matches
      return tx.match.findMany({
        where: { tournamentId: id },
        orderBy: [
          { round: 'asc' },
          { position: 'asc' },
        ],
      });
    });

    return NextResponse.json({
      message: 'Bracket generated successfully',
      tournamentStatus: 'IN_PROGRESS',
      matches: result,
      totalMatches: result.length,
      totalRounds: Math.max(...result.map(m => m.round)),
    });
  } catch (error) {
    console.error('Error generating bracket:', error);
    return NextResponse.json(
      { error: 'Failed to generate bracket' },
      { status: 500 }
    );
  }
}
